Remove unused code and refactor components for React Native
Some checks failed
forgejo/Procyon/procyon/pipeline/head There was a failure building this commit
Some checks failed
forgejo/Procyon/procyon/pipeline/head There was a failure building this commit
This commit removes unused placeholder files and updates several components to be compatible with React Native. These updates include changes in styles, properties, and components like Modals, Toaster, Navigator, and others. The affected files were also appropriately reorganized. Additionally, a new .storybook manager file was introduced.
This commit is contained in:
parent
834f2d3bba
commit
9e0aec7186
90 changed files with 718 additions and 686 deletions
6
.storybook/manager.ts
Normal file
6
.storybook/manager.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { addons } from '@storybook/manager-api';
|
||||
|
||||
addons.setConfig({
|
||||
panelPosition: 'bottom',
|
||||
showToolbar: true,
|
||||
});
|
|
@ -63,13 +63,14 @@ export const parameters = {
|
|||
},
|
||||
options: {
|
||||
storySort: {
|
||||
method: 'alphabetical',
|
||||
method: 'alphabetical-by-kind',
|
||||
order: [
|
||||
'Introduction',
|
||||
'Auth',
|
||||
'Api',
|
||||
['Introduction'],
|
||||
'components',
|
||||
['Inputs', 'Layout', 'Display', 'Utils', '*'],
|
||||
'Form',
|
||||
['Introduction', 'Implementation'],
|
||||
'Localization',
|
||||
|
|
|
@ -38,7 +38,6 @@ export default (config) => {
|
|||
'@procyon/forms': path.join(__dirname, '../packages/forms/src'),
|
||||
'@procyon/hooks': path.join(__dirname, '../packages/hooks/src'),
|
||||
'@procyon/localization': path.join(__dirname, '../packages/localization/src'),
|
||||
'@procyon/router': path.join(__dirname, '../packages/router/src'),
|
||||
'@procyon/styles': path.join(__dirname, '../packages/styles/src'),
|
||||
'@procyon/types': path.join(__dirname, '../packages/types/src'),
|
||||
'@procyon/utils': path.join(__dirname, '../packages/utils/src'),
|
||||
|
|
|
@ -14,7 +14,6 @@ module.exports = {
|
|||
'^@procyon/forms(.*)$': '<rootDir>/packages/forms/src/$1',
|
||||
'^@procyon/hooks(.*)$': '<rootDir>/packages/forms/src/$1',
|
||||
'^@procyon/localization(.*)$': '<rootDir>/packages/localization/src/$1',
|
||||
'^@procyon/router(.*)$': '<rootDir>/packages/router/src/$1',
|
||||
'^@procyon/styles(.*)$': '<rootDir>/packages/styles/src/$1',
|
||||
'^@procyon/types(.*)$': '<rootDir>/packages/types/src/$1',
|
||||
'\\.(css|less|sass|scss)$': '<rootDir>/types/emptyMock.ts',
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
"@storybook/addon-viewport": "7.6.12",
|
||||
"@storybook/addon-react-native-web": "^0.0.22",
|
||||
"@storybook/blocks": "7.6.12",
|
||||
"@storybook/manager-api": "7.6.12",
|
||||
"@storybook/react": "7.6.12",
|
||||
"@storybook/react-native": "7.6.12",
|
||||
"@storybook/react-webpack5": "7.6.12",
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
import React, { FC, ReactNode } from 'react';
|
||||
|
||||
export type PlaceholderProps = {
|
||||
active: boolean;
|
||||
children: ReactNode;
|
||||
component?: ReactNode;
|
||||
};
|
||||
|
||||
export const Placeholder: FC<PlaceholderProps> = ({ children, active, component }) =>
|
||||
active ? <div className="animate-pulse">{component}</div> : children;
|
|
@ -1 +0,0 @@
|
|||
export { Placeholder, type PlaceholderProps } from './Placeholder';
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export const BlockPlaceholder = () => {
|
||||
return (
|
||||
<div className="mt-1">
|
||||
<div className="h-10 bg-gray-200 rounded" />
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export const FieldPlaceholder = () => {
|
||||
return (
|
||||
<div className="mt-1">
|
||||
<div className="h-10 my-[7px] bg-gray-200 rounded" />
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export const TablePlaceholder = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex">
|
||||
<div className="h-8 bg-gray-200 mr-2 rounded w-1/5" />
|
||||
<div className="h-8 bg-gray-200 mx-2 rounded w-1/5" />
|
||||
<div className="h-8 bg-gray-200 mx-2 rounded w-1/5" />
|
||||
<div className="h-8 bg-gray-200 mx-2 rounded w-1/5" />
|
||||
<div className="h-8 bg-gray-200 ml-2 rounded w-1/5" />
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
<div className="h-10 bg-gray-200 rounded" />
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
<div className="h-10 bg-gray-200 rounded" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -5,7 +5,13 @@ import { isJSON } from '@procyon/utils';
|
|||
import { setAuthAttributes } from './slice';
|
||||
import { UseFetchTokensParams, UseReAuthFetchParams } from './types';
|
||||
|
||||
export const fetchTokens = async ({ baseUrl, tokenEndpoint, clientId, code, redirectUri }: UseFetchTokensParams) => {
|
||||
export const changeCodeForTokens = async ({
|
||||
baseUrl,
|
||||
tokenEndpoint,
|
||||
clientId,
|
||||
code,
|
||||
redirectUri,
|
||||
}: UseFetchTokensParams) => {
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}${tokenEndpoint}`, {
|
||||
method: 'post',
|
||||
|
@ -17,7 +23,9 @@ export const fetchTokens = async ({ baseUrl, tokenEndpoint, clientId, code, redi
|
|||
redirect_uri: redirectUri,
|
||||
}),
|
||||
});
|
||||
const data = await response.text().then((text: string) => (isJSON(text) ? JSON.parse(text) : text));
|
||||
const data = await response
|
||||
.text()
|
||||
.then((text: string) => (isJSON(text) ? JSON.parse(text) : text));
|
||||
if (!response.ok) throw response;
|
||||
return { data };
|
||||
} catch (error) {
|
||||
|
@ -38,7 +46,9 @@ export const renewAccessToken = async (
|
|||
refresh_token: refreshToken,
|
||||
}),
|
||||
});
|
||||
const data = await response.text().then((text: string) => (isJSON(text) ? JSON.parse(text) : text));
|
||||
const data = await response
|
||||
.text()
|
||||
.then((text: string) => (isJSON(text) ? JSON.parse(text) : text));
|
||||
if (!response.ok) throw response;
|
||||
api.dispatch(
|
||||
setAuthAttributes({
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useLocation } from 'wouter';
|
||||
|
||||
import { fetchTokens } from '../actions';
|
||||
import { changeCodeForTokens } from '../actions';
|
||||
import { setAuthAttributes } from '../slice';
|
||||
import { UseLoginParams } from '../types';
|
||||
|
||||
export const useFetchTokens = ({ baseUrl, clientId, tokenEndpoint, redirectUri }: UseLoginParams) => {
|
||||
export const useCodeForTokens = ({
|
||||
baseUrl,
|
||||
clientId,
|
||||
tokenEndpoint,
|
||||
redirectUri,
|
||||
}: UseLoginParams) => {
|
||||
const dispatch = useDispatch<any>();
|
||||
|
||||
const [, setLocation] = useLocation();
|
||||
|
||||
return useCallback(async (code: string) => {
|
||||
const { data: tokens } = await fetchTokens({
|
||||
const { data: tokens } = await changeCodeForTokens({
|
||||
baseUrl,
|
||||
tokenEndpoint,
|
||||
clientId,
|
||||
|
@ -31,7 +33,7 @@ export const useFetchTokens = ({ baseUrl, clientId, tokenEndpoint, redirectUri }
|
|||
sessionState: tokens.session_state,
|
||||
}),
|
||||
);
|
||||
setLocation('/');
|
||||
return true;
|
||||
}
|
||||
}, []);
|
||||
};
|
|
@ -24,11 +24,11 @@ export const slice = createSlice({
|
|||
state[action.payload.key] = action.payload.value;
|
||||
},
|
||||
},
|
||||
deleteAuthAttributes: () => initialState,
|
||||
resetAuthAttributes: () => initialState,
|
||||
},
|
||||
});
|
||||
|
||||
export const { deleteAuthAttributes } = slice.actions;
|
||||
export const { resetAuthAttributes } = slice.actions;
|
||||
|
||||
export const setAuthAttributes = slice.actions.setAuthAttributes as <T>(
|
||||
attrs: AuthAttributes<T>,
|
||||
|
@ -39,4 +39,5 @@ export const updateAuthAttribute = slice.actions.updateAuthAttribute as <T>(
|
|||
value: unknown,
|
||||
) => PayloadAction<ModifyAuthAttributePayload<T>>;
|
||||
|
||||
export const getAuthAttributes = <T>(state: AuthRootState<T>) => state.procyon.auth as AuthAttributes<T>;
|
||||
export const getAuthAttributes = <T>(state: AuthRootState<T>) =>
|
||||
state.procyon.auth as AuthAttributes<T>;
|
||||
|
|
|
@ -150,7 +150,7 @@ const stylesSheet = createStyleSheet((theme) => ({
|
|||
};
|
||||
|
||||
return {
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
variants: {
|
||||
size: {
|
||||
[SizeEnum.sm]: variantForSize(SizeEnum.sm),
|
||||
|
|
|
@ -66,17 +66,24 @@ export const Checkbox: React.FC<CheckboxProps> = (props) => {
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setChecked(props.checked || false);
|
||||
if (props.checked ?? false) {
|
||||
setChecked(true);
|
||||
handleAnimation();
|
||||
}
|
||||
}, [props.checked]);
|
||||
|
||||
const handleClick = (e: NativeSyntheticEvent<NativeTouchEvent>) => {
|
||||
sliderSV.value = withTiming(
|
||||
!checked ? SLIDER_RIGHT_POSITION[size].right : SLIDER_RIGHT_POSITION[size].left,
|
||||
);
|
||||
handleAnimation();
|
||||
setChecked(!checked);
|
||||
props.onClick?.(!checked, e);
|
||||
};
|
||||
|
||||
const handleAnimation = () => {
|
||||
sliderSV.value = withTiming(
|
||||
checked ? SLIDER_RIGHT_POSITION[size].left : SLIDER_RIGHT_POSITION[size].right,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[styles.procyon_checkbox_container, style?.container]}
|
||||
|
@ -198,7 +205,7 @@ const styleSheet = createStyleSheet((theme) => {
|
|||
fontSize: theme.fontSize[size],
|
||||
});
|
||||
return {
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
variants: {
|
||||
disabled: {
|
||||
true: {
|
||||
|
|
|
@ -23,7 +23,7 @@ export const DatePicker: React.FC<DatePickerProps> = (props) => {
|
|||
|
||||
const [openModal, closeModal] = useModal(name, {
|
||||
title: label,
|
||||
style: { width: 400 },
|
||||
style: { container: { width: 400 } },
|
||||
});
|
||||
|
||||
const [value, setValue] = useState<Date | null>();
|
||||
|
@ -36,7 +36,7 @@ export const DatePicker: React.FC<DatePickerProps> = (props) => {
|
|||
|
||||
const handleDateClick = (value: Date): void => {
|
||||
setValue(value);
|
||||
onChange?.(value, null);
|
||||
onChange?.(value);
|
||||
};
|
||||
|
||||
const modalComponent = useCallback(
|
||||
|
@ -63,6 +63,7 @@ export const DatePicker: React.FC<DatePickerProps> = (props) => {
|
|||
<TextField
|
||||
name={name}
|
||||
label={label}
|
||||
placeholder={format ?? DefaultDateFormat}
|
||||
value={value ? formatFn(value, props.format ?? DefaultDateFormat) : undefined}
|
||||
{...omit(['value', 'onClick'], others)}
|
||||
leftIcon={<TodayIcon onClick={handleOpenModal} className="field-icon" />}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { FC, ReactNode, useState } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
import { useStyles } from 'react-native-unistyles';
|
||||
|
||||
import { Button } from '@procyon/components/Button';
|
||||
|
|
|
@ -32,7 +32,7 @@ const styleSheet = createStyleSheet((theme) => ({
|
|||
return {
|
||||
padding: theme.spacing.md,
|
||||
paddingTop: 2,
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
variants: {
|
||||
status: {
|
||||
[StatusEnum.none]: variantForStatus(StatusEnum.none),
|
||||
|
|
|
@ -13,7 +13,15 @@ type IGridColProps = {
|
|||
vertical?: GridVerticalAlignEnum;
|
||||
};
|
||||
|
||||
export const GridCol: FC<IGridColProps> = ({ start, end, colSpan, rowSpan, children, className, vertical }) => {
|
||||
export const GridCol: FC<IGridColProps> = ({
|
||||
start,
|
||||
end,
|
||||
colSpan,
|
||||
rowSpan,
|
||||
children,
|
||||
className,
|
||||
vertical,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
2
packages/components/src/Grid/index.ts
Normal file
2
packages/components/src/Grid/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { Grid, GridSizeEnum, GridVerticalAlignEnum } from './components/Grid';
|
||||
export { GridCol } from './components/GridCol';
|
|
@ -25,7 +25,7 @@ const styleSheet = createStyleSheet((theme) => {
|
|||
return {
|
||||
color: theme.components.field.label.color,
|
||||
backgroundColor: theme.components.field.label.background,
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
variants: {
|
||||
disabled: {
|
||||
true: {
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useRef, useState } from 'react';
|
|||
import { Pressable, StyleProp, Text, View, ViewStyle } from 'react-native';
|
||||
import { createStyleSheet, useStyles } from 'react-native-unistyles';
|
||||
|
||||
import { Section } from '@procyon/components/Section';
|
||||
import { Section, SectionProps } from '@procyon/components/Section';
|
||||
import { SizeEnum } from '@procyon/types/enums';
|
||||
import { Option } from '@procyon/types/options';
|
||||
|
||||
|
@ -13,16 +13,17 @@ type ListProps = {
|
|||
className?: string;
|
||||
compact?: boolean;
|
||||
enableAutoScroll?: boolean;
|
||||
mode: 'view' | 'interactive';
|
||||
mode?: 'view' | 'interactive';
|
||||
name: string;
|
||||
noBorder?: boolean;
|
||||
onChange?: (code: Option['code']) => void;
|
||||
options: Option[];
|
||||
scrollToCode?: Option['code'];
|
||||
selectedCode?: Option['code'];
|
||||
style: {
|
||||
container: StyleProp<ViewStyle>;
|
||||
item: StyleProp<ViewStyle>;
|
||||
style?: {
|
||||
container?: StyleProp<ViewStyle>;
|
||||
item?: StyleProp<ViewStyle>;
|
||||
section?: SectionProps['style'];
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -66,37 +67,56 @@ export const List: React.FC<ListProps> = ({
|
|||
const isInteractive = mode === 'interactive';
|
||||
|
||||
return (
|
||||
<View style={[styles.procyon_list_container(), style?.container]}>
|
||||
<>
|
||||
{autoComplete && (
|
||||
<TextField
|
||||
name="search"
|
||||
label="Hledat"
|
||||
onChange={handleSearch}
|
||||
autoFocus
|
||||
style={{
|
||||
container: {
|
||||
marginBottom: 5,
|
||||
},
|
||||
}}
|
||||
size={SizeEnum.sm}
|
||||
/>
|
||||
)}
|
||||
{searchResults.length > 0 ? (
|
||||
searchResults.map(({ code, name, description }) => (
|
||||
<Pressable
|
||||
disabled={mode === 'view'}
|
||||
{...(isInteractive && {
|
||||
onHoverIn: () => setFocused(code),
|
||||
onHoverOut: () => setFocused(undefined),
|
||||
onPress: () => setSelectedOption(code),
|
||||
})}
|
||||
key={code}
|
||||
style={[
|
||||
styles.procyon_list_item(code === focused, code === selected),
|
||||
style?.item,
|
||||
]}>
|
||||
<Section title={name}>{description}</Section>
|
||||
</Pressable>
|
||||
))
|
||||
) : (
|
||||
<Text>Nic k zobrazení</Text>
|
||||
)}
|
||||
</View>
|
||||
<View style={[styles.procyon_list_container(), style?.container]}>
|
||||
{searchResults.length > 0 ? (
|
||||
searchResults.map(({ code, name, description }, i) => {
|
||||
const sibling = searchResults[i - 1]?.code ?? null;
|
||||
return (
|
||||
<Pressable
|
||||
disabled={mode === 'view'}
|
||||
{...(isInteractive && {
|
||||
onHoverIn: () => setFocused(code),
|
||||
onHoverOut: () => setFocused(undefined),
|
||||
onPress: () => setSelectedOption(code),
|
||||
})}
|
||||
key={code}
|
||||
style={[
|
||||
styles.procyon_list_item(
|
||||
code === focused,
|
||||
code === selected,
|
||||
i === 0,
|
||||
i === searchResults.length - 1,
|
||||
sibling === focused,
|
||||
sibling === selected,
|
||||
),
|
||||
style?.item,
|
||||
]}>
|
||||
<Section style={style?.section} title={name}>
|
||||
{description}
|
||||
</Section>
|
||||
</Pressable>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Text>Nic k zobrazení</Text>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -105,11 +125,30 @@ const styleSheet = createStyleSheet((theme) => ({
|
|||
padding: theme.spacing.sm,
|
||||
overflowY: 'auto',
|
||||
}),
|
||||
procyon_list_item: (focused: boolean, selected: boolean) => ({
|
||||
procyon_list_item: (
|
||||
focused: boolean,
|
||||
selected: boolean,
|
||||
isFirst: boolean,
|
||||
isLast: boolean,
|
||||
nextSiblingFocused: boolean,
|
||||
nextSiblingSelected: boolean,
|
||||
) => ({
|
||||
padding: theme.spacing.md,
|
||||
marginVertical: 2,
|
||||
borderRadius: theme.spacing.md,
|
||||
borderWidth: 2,
|
||||
borderBottomWidth: 0,
|
||||
...(isFirst && {
|
||||
borderTopLeftRadius: theme.spacing.md,
|
||||
borderTopRightRadius: theme.spacing.md,
|
||||
}),
|
||||
...(isLast && {
|
||||
borderBottomLeftRadius: theme.spacing.md,
|
||||
borderBottomRightRadius: theme.spacing.md,
|
||||
borderBottomWidth: 2,
|
||||
}),
|
||||
...(!isFirst &&
|
||||
!isLast && {
|
||||
borderRadius: 0,
|
||||
}),
|
||||
...(selected
|
||||
? {
|
||||
backgroundColor: theme.common.active.background,
|
||||
|
@ -123,6 +162,12 @@ const styleSheet = createStyleSheet((theme) => ({
|
|||
: {
|
||||
borderColor: theme.components.list.item.border,
|
||||
}),
|
||||
...(nextSiblingFocused && {
|
||||
borderTopColor: theme.common.focus.border,
|
||||
}),
|
||||
...(nextSiblingSelected && {
|
||||
borderTopColor: theme.common.active.border,
|
||||
}),
|
||||
variants: {
|
||||
compact: {
|
||||
true: {
|
||||
|
|
|
@ -97,12 +97,12 @@ const styleSheet = createStyleSheet((theme) => {
|
|||
flex: 'auto' as any,
|
||||
},
|
||||
procyon_message_title: {
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
fontSize: theme.fontSize.lg,
|
||||
marginBottom: 10,
|
||||
},
|
||||
procyon_message_text: {
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
fontSize: theme.fontSize.sm,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { StatusEnum } from '@procyon/types/enums';
|
||||
|
||||
import { Button } from '../../Button';
|
||||
import { Modal } from '../context';
|
||||
import { useModal } from '../useModal';
|
||||
|
||||
export type AddModalButtonProps = {
|
||||
modalTitle: string;
|
||||
status?: StatusEnum;
|
||||
title: string;
|
||||
} & Modal<any>;
|
||||
|
||||
export const AddModalButton: FC<AddModalButtonProps> = ({ status, title, ...props }) => {
|
||||
const [openModal] = useModal(props.id, {
|
||||
...props,
|
||||
title: props.modalTitle,
|
||||
});
|
||||
return <Button label={title} onClick={openModal} status={status} />;
|
||||
};
|
|
@ -1,4 +1,6 @@
|
|||
import React, { MouseEvent, useEffect } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Pressable, Text, View } from 'react-native';
|
||||
import { createStyleSheet, useStyles } from 'react-native-unistyles';
|
||||
|
||||
import { isNilOrEmpty } from '@procyon/utils';
|
||||
|
||||
|
@ -8,6 +10,8 @@ import { useModal } from '../useModal';
|
|||
export const Modals = () => {
|
||||
const [, , { modals, opened, closeModal }] = useModal();
|
||||
|
||||
const { styles } = useStyles(styleSheet);
|
||||
|
||||
const handleCloseModal = (modalId: ModalId) => {
|
||||
modals[modalId]?.props?.onClose?.();
|
||||
closeModal(modalId);
|
||||
|
@ -33,29 +37,57 @@ export const Modals = () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{opened.map((modalId) => {
|
||||
const { id, style, title, Component, props } = modals[modalId] ?? {};
|
||||
if (!id) return null;
|
||||
return (
|
||||
<div key={id} className="modal-wrapper bg-black bg-opacity-70" onClick={() => handleCloseModal(id)}>
|
||||
<div
|
||||
className="modal-container"
|
||||
style={{
|
||||
width: style?.width,
|
||||
...(style?.overrideMaxWidth ? { maxWidth: 'initial' } : {}),
|
||||
}}>
|
||||
<div className="modal-title">{title}</div>
|
||||
<div className="modal-body" onClick={(e: MouseEvent) => e.stopPropagation()}>
|
||||
{Component && <Component {...props} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
return opened.map((modalId) => {
|
||||
const { id, style, title, Component, props } = modals[modalId] ?? {};
|
||||
if (!id) return null;
|
||||
return (
|
||||
<Pressable
|
||||
key={id}
|
||||
style={[styles.procyon_modal_wrapper, style?.wrapper]}
|
||||
onPress={() => handleCloseModal(id)}>
|
||||
<Pressable
|
||||
style={[styles.procyon_modal_container, style?.container]}
|
||||
onPress={(e) => e.preventDefault()}>
|
||||
{title && (
|
||||
<Text style={[styles.procyon_modal_title, style?.title]}>{title}</Text>
|
||||
)}
|
||||
<View style={[styles.procyon_modal_body, style?.body]}>
|
||||
{Component && <Component {...props} />}
|
||||
</View>
|
||||
</Pressable>
|
||||
</Pressable>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default Modals;
|
||||
const styleSheet = createStyleSheet((theme) => ({
|
||||
procyon_modal_wrapper: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
backgroundColor: 'rgba(57,58,52,0.25)',
|
||||
},
|
||||
procyon_modal_container: {
|
||||
cursor: 'auto',
|
||||
minWidth: 200,
|
||||
marginVertical: 'auto',
|
||||
marginHorizontal: 'auto',
|
||||
padding: theme.spacing.lg,
|
||||
backgroundColor: theme.components.modal.container.background,
|
||||
borderWidth: 2,
|
||||
borderColor: theme.components.modal.container.none.border,
|
||||
borderRadius: theme.spacing.md,
|
||||
},
|
||||
procyon_modal_title: {
|
||||
fontSize: theme.fontSize.lg,
|
||||
fontFamily: theme.fontFamily,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: theme.components.modal.container.none.border,
|
||||
paddingHorizontal: theme.spacing.md,
|
||||
paddingBottom: theme.spacing.md,
|
||||
marginBottom: 10,
|
||||
},
|
||||
procyon_modal_body: {},
|
||||
}));
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import React, { createContext, FC, ReactNode, useEffect, useMemo, useState } from 'react';
|
||||
import { omit } from 'ramda';
|
||||
import { StyleProp, TextStyle, ViewStyle } from 'react-native';
|
||||
|
||||
export type Modal<P> = {
|
||||
Component?: FC<P>;
|
||||
id: string;
|
||||
props?: Partial<P>;
|
||||
style?: {
|
||||
overrideMaxWidth?: boolean;
|
||||
width?: number | string;
|
||||
body?: StyleProp<ViewStyle>;
|
||||
container?: StyleProp<ViewStyle>;
|
||||
title?: StyleProp<TextStyle>;
|
||||
wrapper?: StyleProp<ViewStyle>;
|
||||
};
|
||||
title?: string | ReactNode;
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ import { isNilOrEmpty } from '@procyon/utils';
|
|||
import { MenuItem } from '../types';
|
||||
import { Item } from './Item';
|
||||
|
||||
export type SkeletonProps = {
|
||||
export type NavigatorProps = {
|
||||
children: ReactNode;
|
||||
components?: {
|
||||
footer?: React.ReactElement;
|
||||
|
@ -28,11 +28,18 @@ export type SkeletonProps = {
|
|||
toasterPosition?: ToastersPosition;
|
||||
};
|
||||
|
||||
export const Skeleton: React.FC<SkeletonProps> = ({ children, components = {}, items, toasterPosition = ToastersPosition.TopRight, onHref }) => {
|
||||
export const Navigator: React.FC<NavigatorProps> = ({
|
||||
children,
|
||||
components = {},
|
||||
items,
|
||||
toasterPosition = ToastersPosition.TopRight,
|
||||
onHref,
|
||||
}) => {
|
||||
const { footer } = components;
|
||||
|
||||
const enabledSide = !isNilOrEmpty(items?.side);
|
||||
const enabledTop = !isNilOrEmpty(items?.top) || !isNilOrEmpty(items?.user) || !isNilOrEmpty(items?.logo);
|
||||
const enabledTop =
|
||||
!isNilOrEmpty(items?.top) || !isNilOrEmpty(items?.user) || !isNilOrEmpty(items?.logo);
|
||||
|
||||
const { isMaxMatch: isMobile } = useMediaQuery({ size: SIZES.md });
|
||||
const { isMaxMatch: isTablet } = useMediaQuery({ size: SIZES.lg });
|
||||
|
@ -66,16 +73,25 @@ export const Skeleton: React.FC<SkeletonProps> = ({ children, components = {}, i
|
|||
};
|
||||
|
||||
const topItems = useMemo(() => {
|
||||
return items?.top?.map((item, i) => <Item key={item.href ?? i} onClick={openLink} item={item} />);
|
||||
return items?.top?.map((item, i) => (
|
||||
<Item key={item.href ?? i} onClick={openLink} item={item} />
|
||||
));
|
||||
}, [items?.top]);
|
||||
|
||||
const userItems = useMemo(() => {
|
||||
return items?.user?.map((item, i) => <Item key={item.href ?? i} onClick={openLink} item={item} />);
|
||||
return items?.user?.map((item, i) => (
|
||||
<Item key={item.href ?? i} onClick={openLink} item={item} />
|
||||
));
|
||||
}, [items?.user]);
|
||||
|
||||
const sideItems = useMemo(() => {
|
||||
return items?.side?.map((item, i) => (
|
||||
<Item key={item.href ?? i} onClick={openLink} item={item} collapsed={sideCollapsed} />
|
||||
<Item
|
||||
key={item.href ?? i}
|
||||
onClick={openLink}
|
||||
item={item}
|
||||
collapsed={sideCollapsed}
|
||||
/>
|
||||
));
|
||||
}, [items?.side, sideCollapsed]);
|
||||
|
||||
|
@ -104,7 +120,7 @@ export const Skeleton: React.FC<SkeletonProps> = ({ children, components = {}, i
|
|||
<div
|
||||
className={clsx('side', {
|
||||
'side--collapsed': sideCollapsed,
|
||||
'side--expand': !sideCollapsed
|
||||
'side--expand': !sideCollapsed,
|
||||
})}>
|
||||
<RightToBracketIcon
|
||||
className={clsx('side-toggler', { 'rotate-180': !sideCollapsed })}
|
1
packages/components/src/Navigator/index.ts
Normal file
1
packages/components/src/Navigator/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { Navigator, NavigatorProps } from './components/Navigator';
|
|
@ -220,7 +220,7 @@ const styleSheet = createStyleSheet((theme) => {
|
|||
fontSize: theme.fontSize[size],
|
||||
});
|
||||
return {
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
...(optionDisabled
|
||||
? {
|
||||
color: theme.components.field.disabled.color,
|
||||
|
|
|
@ -7,7 +7,10 @@ import { SizeEnum } from '@procyon/types/enums';
|
|||
export type SectionProps = {
|
||||
children: any;
|
||||
size?: SizeEnum;
|
||||
style?: StyleProp<TextStyle>;
|
||||
style?: {
|
||||
message?: StyleProp<TextStyle>;
|
||||
title?: StyleProp<TextStyle>;
|
||||
};
|
||||
title?: string;
|
||||
};
|
||||
|
||||
|
@ -24,18 +27,20 @@ export const Section: FC<SectionProps> = ({
|
|||
style={[
|
||||
{
|
||||
fontSize: theme.fontSize[size],
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
...(children
|
||||
? {
|
||||
marginBottom: theme.spacing.sm,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
style,
|
||||
style?.title,
|
||||
]}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text style={{ fontFamily: theme.common.fontFamily }}>{children && children}</Text>
|
||||
<Text style={[{ fontFamily: theme.fontFamily }, style?.message]}>
|
||||
{children && children}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { AngleDownIcon } from '@procyon/components/Icons/AngleDown';
|
||||
import { ButtonCursorIcon } from '@procyon/components/Icons/ButtonCursor';
|
||||
import { useModal } from '@procyon/components/Modal/useModal';
|
||||
import { InputElementType } from '@procyon/types/form';
|
||||
|
@ -19,9 +20,11 @@ export type SelectBoxProps<V = Option['code']> = Omit<
|
|||
export const Selectbox: React.FC<SelectBoxProps> = (props) => {
|
||||
const { value, options, onChange, name, label, autoComplete, ...others } = props;
|
||||
|
||||
const [selectedCode, setSelectedCode] = useState<Option['code'] | null>(null);
|
||||
|
||||
const [openModal, closeModal] = useModal(name, {
|
||||
title: label,
|
||||
style: { width: 300 },
|
||||
style: { container: { maxWidth: 300 } },
|
||||
Component: (props: object) => (
|
||||
<SelectboxModal
|
||||
autoComplete={autoComplete}
|
||||
|
@ -30,29 +33,27 @@ export const Selectbox: React.FC<SelectBoxProps> = (props) => {
|
|||
closeModal={closeModal}
|
||||
onChange={handleOptionClick}
|
||||
onRemove={handleOptionRemove}
|
||||
selectedCode={selectedOption?.code}
|
||||
selectedCode={selectedCode ?? undefined}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
});
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState<Option | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (value !== selectedOption?.code) {
|
||||
const newValue = options?.find(({ code }) => code === value) ?? null;
|
||||
setSelectedOption(newValue);
|
||||
if (value !== selectedCode) {
|
||||
const newValue = options?.find(({ code }) => code === value)?.code ?? null;
|
||||
setSelectedCode(newValue);
|
||||
}
|
||||
}, [value, options]);
|
||||
|
||||
const handleOptionClick = (option: Option) => {
|
||||
setSelectedOption(option);
|
||||
onChange?.(option?.code, null);
|
||||
const handleOptionClick = (code: Option['code']) => {
|
||||
setSelectedCode(code);
|
||||
onChange?.(code);
|
||||
};
|
||||
|
||||
const handleOptionRemove = () => {
|
||||
setSelectedOption(null);
|
||||
onChange?.(undefined, null);
|
||||
setSelectedCode(null);
|
||||
onChange?.(undefined);
|
||||
};
|
||||
|
||||
const handleOpenModal = () => {
|
||||
|
@ -65,8 +66,10 @@ export const Selectbox: React.FC<SelectBoxProps> = (props) => {
|
|||
readonly
|
||||
name={name}
|
||||
label={label}
|
||||
value={selectedOption?.name}
|
||||
leftIcon={<ButtonCursorIcon className="field-icon" onClick={handleOpenModal} />}
|
||||
value={options.find(({ code }) => code === selectedCode)?.name}
|
||||
rightIcon={
|
||||
<AngleDownIcon style={{ cursor: 'pointer' }} onClick={handleOpenModal} />
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
import React, { FC } from 'react';
|
||||
import { useStyles } from 'react-native-unistyles';
|
||||
|
||||
import { GridCol } from '@procyon/components/GridCol';
|
||||
import { GridCol } from '@procyon/components/Grid/components/GridCol';
|
||||
import { TrashIcon } from '@procyon/components/Icons/Trash';
|
||||
import { List } from '@procyon/components/List';
|
||||
import { SizeEnum, StatusEnum } from '@procyon/types/enums';
|
||||
import { Option } from '@procyon/types/options';
|
||||
|
||||
import { Button } from '../../Button';
|
||||
import { Grid, GridSizeEnum } from '../../Grid';
|
||||
import { Grid, GridSizeEnum } from '../../Grid/components/Grid';
|
||||
import { ModalId } from '../../Modal/context';
|
||||
|
||||
export type SelectboxModalProps = {
|
||||
autoComplete?: boolean;
|
||||
closeModal: () => void;
|
||||
name: ModalId;
|
||||
onChange: (options: Option) => void;
|
||||
onChange: (code: Option['code']) => void;
|
||||
onRemove: () => void;
|
||||
options: Option[];
|
||||
selectedCode?: Option['code'];
|
||||
|
@ -26,8 +27,10 @@ export const SelectboxModal: FC<SelectboxModalProps> = (props) => {
|
|||
const { name, options, autoComplete, selectedCode, onChange, onRemove, closeModal } =
|
||||
props;
|
||||
|
||||
const handleChange = (option: Option) => {
|
||||
onChange(option);
|
||||
const { theme } = useStyles();
|
||||
|
||||
const handleChange = (code: Option['code']) => {
|
||||
onChange(code);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
|
@ -44,6 +47,16 @@ export const SelectboxModal: FC<SelectboxModalProps> = (props) => {
|
|||
autoComplete={autoComplete}
|
||||
selectedCode={selectedCode}
|
||||
onChange={handleChange}
|
||||
style={{
|
||||
container: {
|
||||
maxHeight: 250,
|
||||
},
|
||||
section: {
|
||||
message: {
|
||||
fontSize: theme.fontSize.sm,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Grid cols={{ [md]: 3 }}>
|
||||
<GridCol start={{ [md]: 3 }} end={{ [md]: 3 }}>
|
||||
|
|
67
packages/components/src/Skeleton.tsx
Normal file
67
packages/components/src/Skeleton.tsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import React, { FC, ReactNode, useEffect } from 'react';
|
||||
import { StyleProp, View, ViewStyle } from 'react-native';
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withRepeat,
|
||||
withSequence,
|
||||
withTiming,
|
||||
} from 'react-native-reanimated';
|
||||
import { useStyles } from 'react-native-unistyles';
|
||||
|
||||
export type PlaceholderProps = {
|
||||
active: boolean;
|
||||
children: ReactNode;
|
||||
layout: ({ key: string } & StyleProp<ViewStyle>)[];
|
||||
style?: {
|
||||
container?: StyleProp<ViewStyle>;
|
||||
};
|
||||
};
|
||||
|
||||
export const Skeleton: FC<PlaceholderProps> = ({ active, children, layout, style }) => {
|
||||
const { theme } = useStyles();
|
||||
|
||||
const opacity = useSharedValue(1);
|
||||
|
||||
const animatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
opacity: opacity.value,
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
opacity.value = withRepeat(
|
||||
withSequence(
|
||||
withTiming(0.4, {
|
||||
duration: 1000,
|
||||
}),
|
||||
withTiming(1, {
|
||||
duration: 1000,
|
||||
}),
|
||||
),
|
||||
Infinity,
|
||||
true,
|
||||
);
|
||||
}, []);
|
||||
|
||||
if (!active) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[style?.container]}>
|
||||
{layout.map(({ key, ...style }: any) => (
|
||||
<Animated.View
|
||||
key={key}
|
||||
style={[
|
||||
{
|
||||
backgroundColor: theme.components.skeleton.animation.background,
|
||||
...style,
|
||||
},
|
||||
animatedStyle,
|
||||
]}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
export { Skeleton, SkeletonProps } from './components/Skeleton';
|
|
@ -27,6 +27,7 @@ export type TextFieldProps<V = string> = {
|
|||
mask?: string;
|
||||
mode?: InputModeOptions;
|
||||
multiline?: boolean;
|
||||
readonly?: boolean;
|
||||
rightIcon?: ReactNode;
|
||||
rightText?: string;
|
||||
secret?: boolean;
|
||||
|
@ -60,12 +61,13 @@ export const TextField: React.FC<TextFieldProps> = (props) => {
|
|||
rightText,
|
||||
message,
|
||||
style,
|
||||
readonly,
|
||||
size = SizeEnum.md,
|
||||
secret,
|
||||
} = props;
|
||||
const [value, setValue] = useState<typeof props.value>('');
|
||||
|
||||
const { styles } = useStyles(styleSheet, {
|
||||
const { styles, theme } = useStyles(styleSheet, {
|
||||
disabled,
|
||||
status,
|
||||
size,
|
||||
|
@ -137,10 +139,11 @@ export const TextField: React.FC<TextFieldProps> = (props) => {
|
|||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
autoFocus={autoFocus}
|
||||
readOnly={disabled}
|
||||
readOnly={readonly || disabled}
|
||||
defaultValue={value}
|
||||
multiline={multiline}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={theme.components.field.disabled.color}
|
||||
numberOfLines={(value?.match(/\n/g) ?? []).length + 1}
|
||||
autoComplete="off"
|
||||
/>
|
||||
|
@ -191,7 +194,7 @@ const styleSheet = createStyleSheet((theme) => {
|
|||
color: theme.components.field.input.color,
|
||||
backgroundColor: theme.components.field.input.background,
|
||||
borderWidth: 2,
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
borderRadius: theme.spacing.md,
|
||||
borderTopLeftRadius: leftSide ? 0 : theme.spacing.md,
|
||||
borderBottomLeftRadius: leftSide ? 0 : theme.spacing.md,
|
||||
|
@ -237,7 +240,7 @@ const styleSheet = createStyleSheet((theme) => {
|
|||
});
|
||||
return {
|
||||
borderWidth: 2,
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
...(type === 'icon'
|
||||
? {
|
||||
justifyContent: 'center',
|
||||
|
@ -292,7 +295,7 @@ const styleSheet = createStyleSheet((theme) => {
|
|||
return {
|
||||
padding: theme.spacing.md,
|
||||
paddingTop: 2,
|
||||
fontFamily: theme.common.fontFamily,
|
||||
fontFamily: theme.fontFamily,
|
||||
variants: {
|
||||
status: {
|
||||
[StatusEnum.none]: variantForStatus(StatusEnum.none),
|
||||
|
|
|
@ -37,12 +37,12 @@ export const TimePicker: FC<TimePickerProps> = (props) => {
|
|||
|
||||
const handleTimeClick = (value: Date): void => {
|
||||
setValue(value);
|
||||
onChange?.(value, null);
|
||||
onChange?.(value);
|
||||
};
|
||||
|
||||
const [openModal] = useModal(name, {
|
||||
title: label,
|
||||
style: { width: 220 },
|
||||
style: { container: { width: 200 } },
|
||||
});
|
||||
|
||||
const modalComponent = useCallback(
|
||||
|
@ -69,6 +69,7 @@ export const TimePicker: FC<TimePickerProps> = (props) => {
|
|||
{...others}
|
||||
name={name}
|
||||
label={label}
|
||||
placeholder="HH:mm"
|
||||
value={value ? format(value, 'HH:mm') : undefined}
|
||||
leftIcon={<ClockTimeIcon onClick={handleOpenModal} className="field-icon" />}
|
||||
/>
|
||||
|
|
|
@ -2,8 +2,8 @@ import React, { useEffect, useState } from 'react';
|
|||
import { format, setHours, setMinutes, setSeconds } from 'date-fns';
|
||||
|
||||
import { Button } from '@procyon/components/Button';
|
||||
import { Grid, GridSizeEnum } from '@procyon/components/Grid';
|
||||
import { GridCol } from '@procyon/components/GridCol';
|
||||
import { Grid, GridSizeEnum } from '@procyon/components/Grid/components/Grid';
|
||||
import { GridCol } from '@procyon/components/Grid/components/GridCol';
|
||||
import { TrashIcon } from '@procyon/components/Icons/Trash';
|
||||
import { useModal } from '@procyon/components/Modal/useModal';
|
||||
import { SizeEnum, StatusEnum } from '@procyon/types/enums';
|
||||
|
@ -99,7 +99,12 @@ export const TimePickerModal: React.FC<TimePickerModalProps> = (props) => {
|
|||
</Grid>
|
||||
<Grid cols={{ [base]: 2 }} className="mt-4">
|
||||
<GridCol>
|
||||
<Button size={SizeEnum.sm} label="Použít" status={StatusEnum.success} onClick={handleChangeValue} />
|
||||
<Button
|
||||
size={SizeEnum.sm}
|
||||
label="Použít"
|
||||
status={StatusEnum.success}
|
||||
onClick={handleChangeValue}
|
||||
/>
|
||||
</GridCol>
|
||||
<GridCol>
|
||||
<Button
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import React, { FC } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { createStyleSheet, useStyles } from 'react-native-unistyles';
|
||||
|
||||
import { isNilOrEmpty } from '@procyon/utils';
|
||||
|
||||
|
@ -21,25 +22,19 @@ export type ToastersProps = {
|
|||
position: ToastersPosition;
|
||||
};
|
||||
|
||||
export function Toasters({ position }) {
|
||||
export const Toasters: FC<ToastersProps> = ({ position }) => {
|
||||
const { toasters } = useToaster();
|
||||
|
||||
const { styles } = useStyles(styleSheet, {
|
||||
position,
|
||||
});
|
||||
|
||||
if (isNilOrEmpty(toasters)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx('toasters', {
|
||||
'toasters--topLeft': ToastersPosition.TopLeft === position,
|
||||
'toasters--topCenter': ToastersPosition.TopCenter === position,
|
||||
'toasters--topRight': ToastersPosition.TopRight === position,
|
||||
'toasters--rightCenter': ToastersPosition.RightCenter === position,
|
||||
'toasters--bottomRight': ToastersPosition.BottomRight === position,
|
||||
'toasters--bottomCenter': ToastersPosition.BottomCenter === position,
|
||||
'toasters--bottomLeft': ToastersPosition.BottomLeft === position,
|
||||
'toasters--leftCenter': ToastersPosition.LeftCenter === position,
|
||||
})}>
|
||||
<View style={[styles.procyon_toasters_container]}>
|
||||
{Object.keys(toasters).map((toasterId) => {
|
||||
const toaster = toasters[toasterId];
|
||||
return (
|
||||
|
@ -48,6 +43,73 @@ export function Toasters({ position }) {
|
|||
</Message>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const styleSheet = createStyleSheet(() => ({
|
||||
procyon_toasters_container: {
|
||||
position: 'absolute',
|
||||
zIndex: 10,
|
||||
variants: {
|
||||
position: {
|
||||
[ToastersPosition.TopLeft]: {
|
||||
top: 5,
|
||||
left: 5,
|
||||
},
|
||||
[ToastersPosition.TopCenter]: {
|
||||
top: 5,
|
||||
left: '50%',
|
||||
transform: [
|
||||
{
|
||||
translateX: -50,
|
||||
translateY: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
[ToastersPosition.TopRight]: {
|
||||
top: 5,
|
||||
right: 5,
|
||||
},
|
||||
[ToastersPosition.RightCenter]: {
|
||||
top: '50%',
|
||||
transform: [
|
||||
{
|
||||
translateY: -50,
|
||||
translateX: 0,
|
||||
},
|
||||
],
|
||||
right: 5,
|
||||
},
|
||||
[ToastersPosition.BottomRight]: {
|
||||
bottom: 5,
|
||||
right: 5,
|
||||
},
|
||||
[ToastersPosition.BottomCenter]: {
|
||||
bottom: 5,
|
||||
left: '50%',
|
||||
transform: [
|
||||
{
|
||||
translateX: -50,
|
||||
translateY: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
[ToastersPosition.BottomLeft]: {
|
||||
bottom: 5,
|
||||
left: 5,
|
||||
},
|
||||
[ToastersPosition.LeftCenter]: {
|
||||
top: '50%',
|
||||
transform: [
|
||||
{
|
||||
translateY: -50,
|
||||
translateX: 0,
|
||||
},
|
||||
],
|
||||
left: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
|
|
@ -18,7 +18,7 @@ type ToasterContextType = {
|
|||
toasters: Record<ToasterId, Toaster>;
|
||||
};
|
||||
|
||||
const DefaultTimeout = 6000;
|
||||
const DefaultTimeout = 6 * 1000;
|
||||
const timeouts: { [key: ToasterId]: any } = [];
|
||||
|
||||
export const ToasterContext = createContext<ToasterContextType>({
|
||||
|
@ -43,7 +43,7 @@ export const ToasterWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
|||
clearTimeout(timeouts[timeoutId]);
|
||||
});
|
||||
},
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -57,7 +57,7 @@ export const ToasterWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
|||
toasters,
|
||||
open: autoHide,
|
||||
}),
|
||||
[toasters]
|
||||
[toasters],
|
||||
);
|
||||
|
||||
return <ToasterContext.Provider value={value}>{children}</ToasterContext.Provider>;
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"name": "@procyon/router",
|
||||
"version": "1.2.10",
|
||||
"description": "",
|
||||
"author": "romanjaros <jarosr93@gmail.com>",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist/ && npm run build -- --clean",
|
||||
"build": "tsc --b tsconfig.build.json",
|
||||
"prepublish": "cp package.json dist",
|
||||
"postpublish": "rm dist/package.json",
|
||||
"release": "npm version --no-git-tag-version"
|
||||
},
|
||||
"publishConfig": {
|
||||
"directory": "dist",
|
||||
"linkDirectory": false
|
||||
},
|
||||
"dependencies": {
|
||||
"@procyon/types": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"wouter": "2.12.1"
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import React, { FC } from 'react';
|
||||
import { Router, useLocation, useRouter } from 'wouter';
|
||||
|
||||
export type RouterProps = {
|
||||
base: string;
|
||||
children: any;
|
||||
};
|
||||
|
||||
export const NestedRouter: FC<RouterProps> = ({ base, children }) => {
|
||||
const router = useRouter();
|
||||
const [location] = useLocation();
|
||||
return location.startsWith(base) ? <Router base={router.base + base}>{children}</Router> : null;
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { DotNestedKeys } from '@procyon/types';
|
||||
|
||||
export const buildRoute = (source: Record<string, any>, route: DotNestedKeys<typeof source>) => {
|
||||
let path = '';
|
||||
let obj = source;
|
||||
route.split('.').forEach((key: any) => {
|
||||
if (key !== 'base') {
|
||||
path += obj[key]?.base ?? obj[key];
|
||||
obj = obj[key];
|
||||
}
|
||||
});
|
||||
return path;
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"composite": true,
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": [
|
||||
"./src"
|
||||
],
|
||||
"exclude": [
|
||||
"**/__tests__/**/*",
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../types/tsconfig.build.json"
|
||||
}
|
||||
],
|
||||
}
|
|
@ -5,42 +5,7 @@ const creator: PluginCreator = ({ addComponents, theme }) =>
|
|||
'.toasters': {
|
||||
position: 'fixed',
|
||||
zIndex: '10',
|
||||
'&--topLeft': {
|
||||
top: '3rem',
|
||||
left: '3rem',
|
||||
},
|
||||
'&--topCenter': {
|
||||
top: '3rem',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, 0%)',
|
||||
},
|
||||
'&--topRight': {
|
||||
top: '3rem',
|
||||
right: '3rem',
|
||||
},
|
||||
'&--rightCenter': {
|
||||
top: '50%',
|
||||
transform: 'translate(0%, -50%)',
|
||||
right: '3rem',
|
||||
},
|
||||
'&--bottomRight': {
|
||||
bottom: '3rem',
|
||||
right: '3rem',
|
||||
},
|
||||
'&--bottomCenter': {
|
||||
bottom: '3rem',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, 0%)',
|
||||
},
|
||||
'&--bottomLeft': {
|
||||
bottom: '3rem',
|
||||
left: '3rem',
|
||||
},
|
||||
'&--leftCenter': {
|
||||
top: '50%',
|
||||
transform: 'translate(0%, -50%)',
|
||||
left: '3rem',
|
||||
},
|
||||
|
||||
[`@media (max-width: ${theme('screens.sm-m.max')})`]: {
|
||||
left: '2rem',
|
||||
with: 'auto',
|
||||
|
|
|
@ -2,7 +2,6 @@ import palette from '../style/palette';
|
|||
|
||||
export const lightTheme = {
|
||||
common: {
|
||||
fontFamily: 'Quicksand',
|
||||
active: {
|
||||
color: palette.pink['600'],
|
||||
background: palette.pink['100'],
|
||||
|
@ -15,6 +14,11 @@ export const lightTheme = {
|
|||
},
|
||||
},
|
||||
components: {
|
||||
skeleton: {
|
||||
animation: {
|
||||
background: palette.gray['200'],
|
||||
},
|
||||
},
|
||||
button: {
|
||||
blank: {
|
||||
color: '#000',
|
||||
|
@ -129,7 +133,27 @@ export const lightTheme = {
|
|||
border: palette.gray['300'],
|
||||
},
|
||||
item: {
|
||||
border: palette.gray['100'],
|
||||
border: palette.gray['200'],
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
container: {
|
||||
background: '#fff',
|
||||
none: {
|
||||
border: palette.gray['300'],
|
||||
},
|
||||
info: {
|
||||
border: palette.azure['300'],
|
||||
},
|
||||
success: {
|
||||
border: palette.submarine['300'],
|
||||
},
|
||||
warning: {
|
||||
border: palette.tangerine['300'],
|
||||
},
|
||||
error: {
|
||||
border: palette.cerise['300'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -145,4 +169,5 @@ export const lightTheme = {
|
|||
lg: 22,
|
||||
xl: 26,
|
||||
},
|
||||
fontFamily: 'Quicksand',
|
||||
} as const;
|
||||
|
|
|
@ -7,17 +7,18 @@ body {
|
|||
}
|
||||
|
||||
.scrollable-element {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #ddd;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #666;
|
||||
border-radius: 5px;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
export type Action<P, T = string> = {
|
||||
readonly payload?: P;
|
||||
readonly type: T;
|
||||
};
|
|
@ -1,7 +1,5 @@
|
|||
import { isNilOrEmpty } from './isNilOrEmpty';
|
||||
import { isJSON } from './json';
|
||||
import { logVal } from './logVal';
|
||||
import { masker } from './masker';
|
||||
import { noop } from './noop';
|
||||
|
||||
export { isNilOrEmpty, isJSON, noop, logVal, masker };
|
||||
export { isNilOrEmpty, isJSON, masker };
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
export const logVal = (x: any) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(x);
|
||||
return x;
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
export const noop = (): void => {
|
||||
/* function is empty */
|
||||
};
|
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
|
@ -81,6 +81,9 @@ importers:
|
|||
'@storybook/blocks':
|
||||
specifier: 7.6.12
|
||||
version: 7.6.12(@types/react-dom@18.2.18)(@types/react@18.2.52)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@storybook/manager-api':
|
||||
specifier: 7.6.12
|
||||
version: 7.6.12(react-dom@18.2.0)(react@18.2.0)
|
||||
'@storybook/react':
|
||||
specifier: 7.6.12
|
||||
version: 7.6.12(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
|
||||
|
@ -233,9 +236,6 @@ importers:
|
|||
|
||||
packages/components:
|
||||
dependencies:
|
||||
'@procyon/constants':
|
||||
specifier: workspace:^
|
||||
version: link:../constants
|
||||
'@procyon/hooks':
|
||||
specifier: workspace:^
|
||||
version: link:../hooks
|
||||
|
@ -280,9 +280,6 @@ importers:
|
|||
version: 2.12.1(react@18.2.0)
|
||||
publishDirectory: dist
|
||||
|
||||
packages/constants:
|
||||
publishDirectory: dist
|
||||
|
||||
packages/forms:
|
||||
dependencies:
|
||||
'@hookform/resolvers':
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { Placeholder } from '@procyon/api/components/Placeholder/Placeholder';
|
||||
import { BlockPlaceholder } from '@procyon/api/components/Placeholder/shapes/Block';
|
||||
import { TablePlaceholder } from '@procyon/api/components/Placeholder/shapes/Table';
|
||||
|
||||
type Story = StoryObj<typeof Placeholder>;
|
||||
|
||||
export default {
|
||||
component: Placeholder,
|
||||
argTypes: {
|
||||
placeholder: {
|
||||
control: { type: null },
|
||||
description: 'Custom React component',
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: "`import { FetchLoader } from '@procyon/components/Loader';`",
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
} as Meta;
|
||||
|
||||
export const Table: Story = {
|
||||
args: {
|
||||
active: true,
|
||||
children: 'children',
|
||||
component: <TablePlaceholder />,
|
||||
},
|
||||
};
|
||||
|
||||
export const Block: Story = {
|
||||
args: {
|
||||
active: true,
|
||||
children: 'children',
|
||||
component: <BlockPlaceholder />,
|
||||
},
|
||||
};
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Placeholder } from '@procyon/api/components/Placeholder/Placeholder';
|
||||
import { Button } from '@procyon/components/Button';
|
||||
import { Grid, GridSizeEnum } from '@procyon/components/Grid';
|
||||
import { GridCol } from '@procyon/components/GridCol';
|
||||
import { Grid, GridSizeEnum } from '@procyon/components/Grid/components/Grid';
|
||||
import { GridCol } from '@procyon/components/Grid/components/GridCol';
|
||||
import { Skeleton } from '@procyon/components/Skeleton';
|
||||
import { StatusEnum } from '@procyon/types/enums';
|
||||
|
||||
import { useLazyGetTodosQuery } from './api';
|
||||
|
@ -22,12 +22,17 @@ export const Component = (): React.ReactElement => {
|
|||
<>
|
||||
<Grid cols={{ [md]: 4 }}>
|
||||
<GridCol>
|
||||
<Button status={StatusEnum.warning} label="Odeslat" onClick={handleFetch} disabled={isFetching} />
|
||||
<Button
|
||||
status={StatusEnum.warning}
|
||||
label="Odeslat"
|
||||
onClick={handleFetch}
|
||||
disabled={isFetching}
|
||||
/>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Placeholder active={isFetching} component={<CustomPlaceholder />}>
|
||||
<Skeleton active={isFetching} component={<CustomPlaceholder />}>
|
||||
<pre>{JSON.stringify(data, undefined, 2)}</pre>
|
||||
</Placeholder>
|
||||
</Skeleton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { FC } from 'react';
|
||||
import { useLocation } from 'wouter';
|
||||
|
||||
import { useFetchTokens } from '@procyon/auth/hook/useFetchTokens';
|
||||
import { useCodeForTokens } from '@procyon/auth/hook/useCodeForTokens';
|
||||
import { useLogin } from '@procyon/auth/hook/useLogin';
|
||||
|
||||
const authConfig = {
|
||||
|
@ -17,7 +17,7 @@ export const Component: FC = () => {
|
|||
const [location] = useLocation();
|
||||
|
||||
const doLogin = useLogin(authConfig);
|
||||
const obtainTokens = useFetchTokens(authConfig);
|
||||
const obtainTokens = useCodeForTokens(authConfig);
|
||||
|
||||
if (location === '/login/callback') {
|
||||
const params = new URLSearchParams(window.location.href);
|
||||
|
|
|
@ -3,11 +3,14 @@ import { action } from '@storybook/addon-actions';
|
|||
import type { StoryObj } from '@storybook/react';
|
||||
|
||||
import { Calendar } from '@procyon/components/Calendar';
|
||||
import { CalendarLayoutEnum, CalendarTypeEnum } from '@procyon/components/Calendar/components/Calendar';
|
||||
import {
|
||||
CalendarLayoutEnum,
|
||||
CalendarTypeEnum,
|
||||
} from '@procyon/components/Calendar/components/Calendar';
|
||||
|
||||
import { MonthView } from './Calendar/MonthView';
|
||||
import { TimeColumn } from './Calendar/TimeColumn';
|
||||
import { WeekView } from './Calendar/WeekView';
|
||||
import { MonthView } from '../__/Calendar/MonthView';
|
||||
import { TimeColumn } from '../__/Calendar/TimeColumn';
|
||||
import { WeekView } from '../__/Calendar/WeekView';
|
||||
|
||||
type Story = StoryObj<typeof Calendar>;
|
||||
|
|
@ -19,7 +19,7 @@ export default {
|
|||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
Title: 'Section',
|
||||
title: 'Section',
|
||||
children: 'Content',
|
||||
},
|
||||
};
|
||||
|
@ -32,7 +32,7 @@ export const WithoutTitle: Story = {
|
|||
|
||||
export const Medium: Story = {
|
||||
args: {
|
||||
Title: 'Section',
|
||||
title: 'Section',
|
||||
size: SizeEnum.md,
|
||||
children: 'content',
|
||||
},
|
||||
|
@ -40,7 +40,7 @@ export const Medium: Story = {
|
|||
|
||||
export const Large: Story = {
|
||||
args: {
|
||||
Title: 'Section',
|
||||
title: 'Section',
|
||||
size: SizeEnum.lg,
|
||||
children: 'content',
|
||||
},
|
||||
|
@ -48,7 +48,7 @@ export const Large: Story = {
|
|||
|
||||
export const ExtraLarge: Story = {
|
||||
args: {
|
||||
Title: 'Section',
|
||||
title: 'Section',
|
||||
size: SizeEnum.xl,
|
||||
children: 'content',
|
||||
},
|
30
stories/components/Display/Skeleton.stories.tsx
Normal file
30
stories/components/Display/Skeleton.stories.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { Skeleton } from '@procyon/components/Skeleton';
|
||||
|
||||
type Story = StoryObj<typeof Skeleton>;
|
||||
|
||||
export default {
|
||||
component: Skeleton,
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: "`import { Skeleton } from '@procyon/components/Skeleton';`",
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
} as Meta;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
active: true,
|
||||
children: null,
|
||||
layout: [
|
||||
{ key: '1', width: 100, height: 100, borderRadius: 50 },
|
||||
{ key: '2', width: 100, height: 20, marginTop: 20 },
|
||||
{ key: '2', width: 120, height: 20, marginTop: 2 },
|
||||
{ key: '2', width: 80, height: 20, marginTop: 2 },
|
||||
],
|
||||
},
|
||||
};
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { View } from 'react-native';
|
||||
|
||||
import { Button } from '@procyon/components/Button';
|
||||
import { Dropdown } from '@procyon/components/Dropdown';
|
||||
|
@ -26,9 +27,9 @@ export default {
|
|||
},
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div className="h-40">
|
||||
<View style={{ height: 200 }}>
|
||||
<Story />
|
||||
</div>
|
||||
</View>
|
||||
),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
|
@ -37,19 +38,12 @@ export default {
|
|||
export const Default: Story = {
|
||||
args: {
|
||||
MainButton: <Button label={'Main action'} />,
|
||||
Buttons: ({ onClose, props }) => {
|
||||
return (
|
||||
<>
|
||||
<Button {...props} label="Button A" onClick={() => onClose()} />
|
||||
<Button
|
||||
{...props}
|
||||
label="Button B"
|
||||
onClick={() => onClose()}
|
||||
status={StatusEnum.error}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
},
|
||||
Buttons: ({ onClose, props }) => (
|
||||
<>
|
||||
<Button {...props} label="Button A" onClick={onClose} />
|
||||
<Button {...props} label="Button B" onClick={onClose} status={StatusEnum.error} />
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
69
stories/components/Inputs/Selectbox.stories.tsx
Normal file
69
stories/components/Inputs/Selectbox.stories.tsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
import React from 'react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { Modals } from '@procyon/components/Modal/components/Modals';
|
||||
import { ModalWrapper } from '@procyon/components/Modal/context';
|
||||
import { Selectbox } from '@procyon/components/Selectbox';
|
||||
|
||||
type Story = StoryObj<typeof Selectbox>;
|
||||
|
||||
const description = `
|
||||
import Selectbox from '@procyon/components/Selectbox';\`
|
||||
|
||||
You need to wrap this component with \`ModalWrapper\` component.
|
||||
`;
|
||||
|
||||
export default {
|
||||
component: Selectbox,
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: description,
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ height: '35rem' }}>
|
||||
<ModalWrapper>
|
||||
<Story />
|
||||
<Modals />
|
||||
</ModalWrapper>
|
||||
</div>
|
||||
),
|
||||
],
|
||||
} as Meta;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
name: 'selectbox',
|
||||
label: 'Zvolte den v týdnu',
|
||||
options: [
|
||||
{ name: 'Pondělí', code: 'po', description: 'Monday in Czech' },
|
||||
{ name: 'Úterý', code: 'ut', description: 'Tuesday in Czech' },
|
||||
{ name: 'Středa', code: 'st', description: 'Wednesday in Czech' },
|
||||
{ name: 'Čtvrtek', code: 'ct', description: 'Thursday in Czech' },
|
||||
{ name: 'Pátek', code: 'pa', description: 'Friday in Czech' },
|
||||
{ name: 'Sobota', code: 'so', description: 'Saturday in Czech' },
|
||||
{ name: 'Neděle', code: 'ne', description: 'Sunday in Czech' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const Atocomplete: Story = {
|
||||
args: {
|
||||
autoComplete: true,
|
||||
name: 'selectboxAutoComplete',
|
||||
label: 'Zvolte den v týdnu',
|
||||
options: [
|
||||
{ name: 'Pondělí', code: 'po', description: 'Monday in Czech' },
|
||||
{ name: 'Úterý', code: 'ut', description: 'Tuesday in Czech' },
|
||||
{ name: 'Středa', code: 'st', description: 'Wednesday in Czech' },
|
||||
{ name: 'Čtvrtek', code: 'ct', description: 'Thursday in Czech' },
|
||||
{ name: 'Pátek', code: 'pa', description: 'Friday in Czech' },
|
||||
{ name: 'Sobota', code: 'so', description: 'Saturday in Czech' },
|
||||
{ name: 'Neděle', code: 'ne', description: 'Sunday in Czech' },
|
||||
],
|
||||
},
|
||||
};
|
|
@ -2,8 +2,12 @@ import React from 'react';
|
|||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { Grid, GridSizeEnum, GridVerticalAlignEnum } from '@procyon/components/Grid';
|
||||
import { GridCol } from '@procyon/components/GridCol';
|
||||
import {
|
||||
Grid,
|
||||
GridCol,
|
||||
GridSizeEnum,
|
||||
GridVerticalAlignEnum,
|
||||
} from '@procyon/components/Grid';
|
||||
|
||||
type Story = StoryObj<typeof Grid>;
|
||||
|
||||
|
@ -15,8 +19,8 @@ export default {
|
|||
component: Grid,
|
||||
tags: ['autodocs'],
|
||||
args: {
|
||||
children: <ColoredDiv> </ColoredDiv>
|
||||
}
|
||||
children: <ColoredDiv> </ColoredDiv>,
|
||||
},
|
||||
} as Meta<typeof Grid>;
|
||||
|
||||
const { sm, lg } = GridSizeEnum;
|
||||
|
@ -109,7 +113,7 @@ export const Default: Story = {
|
|||
<ColoredDiv> </ColoredDiv>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const Left: Story = {
|
||||
|
@ -119,7 +123,7 @@ export const Left: Story = {
|
|||
<ColoredDiv> </ColoredDiv>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const Center: Story = {
|
||||
|
@ -129,7 +133,7 @@ export const Center: Story = {
|
|||
<ColoredDiv> </ColoredDiv>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const Right: Story = {
|
||||
|
@ -139,7 +143,7 @@ export const Right: Story = {
|
|||
<ColoredDiv> </ColoredDiv>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const Top: Story = {
|
||||
|
@ -159,7 +163,7 @@ export const Top: Story = {
|
|||
<ColoredDiv> </ColoredDiv>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const Middle: Story = {
|
||||
|
@ -179,7 +183,7 @@ export const Middle: Story = {
|
|||
<ColoredDiv> </ColoredDiv>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const Bottom: Story = {
|
||||
|
@ -199,7 +203,7 @@ export const Bottom: Story = {
|
|||
<ColoredDiv> </ColoredDiv>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const Responsive: Story = {
|
||||
|
@ -219,5 +223,5 @@ export const Responsive: Story = {
|
|||
<ColoredDiv> </ColoredDiv>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
)
|
||||
),
|
||||
};
|
|
@ -22,26 +22,16 @@ return (
|
|||
);
|
||||
```
|
||||
|
||||
## Use `AddModalButton`
|
||||
|
||||
```tsx
|
||||
import { AddModalButton } from '@procyon/components/Modal/components/AddModalButton';
|
||||
|
||||
return <AddModalButton />;
|
||||
```
|
||||
|
||||
<Canvas of={ModalStories.Default} withToolbar className='h-80' />
|
||||
|
||||
## Use hook `useModal()`
|
||||
|
||||
```tsx
|
||||
import { Text } from 'react-native';
|
||||
import { useModal } from '@procyon/components/Modal/useModal';
|
||||
|
||||
const [openModal] = useModal({
|
||||
component: <div>Modal content</div>,
|
||||
component: <Text>Modal content</Text>,
|
||||
id: 'modal1',
|
||||
props: { onClick: () => {} },
|
||||
style: { width: '300px' },
|
||||
title: 'Modal Title',
|
||||
});
|
||||
|
58
stories/components/Layout/Modals.stories.tsx
Normal file
58
stories/components/Layout/Modals.stories.tsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
import React from 'react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import { Button } from '@procyon/components/Button';
|
||||
import { Modals } from '@procyon/components/Modal';
|
||||
import {
|
||||
ModalWrapper,
|
||||
openModal as openOutsideReactModal,
|
||||
} from '@procyon/components/Modal/context';
|
||||
import { useModal } from '@procyon/components/Modal/useModal';
|
||||
|
||||
type Story = StoryObj<typeof Modals>;
|
||||
|
||||
export default {
|
||||
component: Modals,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<View style={{ height: 500 }}>
|
||||
<ModalWrapper>
|
||||
<Story />
|
||||
<Modals />
|
||||
</ModalWrapper>
|
||||
</View>
|
||||
),
|
||||
],
|
||||
} as Meta;
|
||||
|
||||
export const Hook: Story = {
|
||||
name: 'Hook',
|
||||
render: () => {
|
||||
const Comp = () => {
|
||||
const [openModal] = useModal('modal2', {
|
||||
Component: () => <Text>Hi there</Text>,
|
||||
title: 'Modal Title',
|
||||
});
|
||||
return <Button label="Open Modal" onClick={openModal} />;
|
||||
};
|
||||
return <Comp />;
|
||||
},
|
||||
};
|
||||
|
||||
export const OutsideReact: Story = {
|
||||
name: 'Function',
|
||||
render: () => {
|
||||
return (
|
||||
<Button
|
||||
label="Open Modal"
|
||||
onClick={() =>
|
||||
openOutsideReactModal({
|
||||
Component: () => <Text>Hi there</Text>,
|
||||
id: 'modal3',
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
|
@ -5,7 +5,7 @@ import { Route } from 'wouter';
|
|||
import { DashboardIcon } from '@procyon/components/Icons/Dashboard';
|
||||
import { SettingsIcon } from '@procyon/components/Icons/Settings';
|
||||
import { UserIcon } from '@procyon/components/Icons/User';
|
||||
import { Skeleton } from '@procyon/components/Skeleton';
|
||||
import { Navigator } from '@procyon/components/Navigator';
|
||||
|
||||
const Routes = () => (
|
||||
<>
|
||||
|
@ -18,7 +18,7 @@ const Routes = () => (
|
|||
</>
|
||||
);
|
||||
|
||||
type Story = StoryObj<typeof Skeleton>;
|
||||
type Story = StoryObj<typeof Navigator>;
|
||||
|
||||
const items = {
|
||||
logo: <div>procyon</div>,
|
||||
|
@ -26,55 +26,55 @@ const items = {
|
|||
{ label: 'Dashboard', href: '/r1', icon: <DashboardIcon /> },
|
||||
{
|
||||
label: 'Uživatelé',
|
||||
href: '/r2'
|
||||
href: '/r2',
|
||||
},
|
||||
{
|
||||
label: 'Skupiny',
|
||||
href: '/r3'
|
||||
}
|
||||
href: '/r3',
|
||||
},
|
||||
],
|
||||
top: [
|
||||
{ label: 'Dashboard', href: '/r1', icon: <DashboardIcon /> },
|
||||
{
|
||||
label: 'Uživatelé',
|
||||
href: '/r2'
|
||||
href: '/r2',
|
||||
},
|
||||
{
|
||||
label: 'Skupiny',
|
||||
href: '/r3'
|
||||
href: '/r3',
|
||||
},
|
||||
{
|
||||
label: 'Menu External',
|
||||
href: 'https://procyon.romanjaros.dev',
|
||||
external: true
|
||||
}
|
||||
external: true,
|
||||
},
|
||||
],
|
||||
user: [
|
||||
{ label: null, icon: <SettingsIcon /> },
|
||||
{
|
||||
label: 'Profil',
|
||||
icon: <UserIcon />
|
||||
}
|
||||
]
|
||||
icon: <UserIcon />,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default {
|
||||
component: Skeleton,
|
||||
component: Navigator,
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: "`import { Skeleton } from '@procyon/components/Skeleton';`"
|
||||
}
|
||||
}
|
||||
component: "`import { Navigator } from '@procyon/components/Navigator';`",
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: ['autodocs']
|
||||
tags: ['autodocs'],
|
||||
} as Meta;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: <Routes />,
|
||||
items: items
|
||||
}
|
||||
items: items,
|
||||
},
|
||||
};
|
||||
|
||||
export const TopPanel: Story = {
|
||||
|
@ -82,9 +82,9 @@ export const TopPanel: Story = {
|
|||
children: <Routes />,
|
||||
items: {
|
||||
...items,
|
||||
side: undefined
|
||||
}
|
||||
}
|
||||
side: undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const SidePanel: Story = {
|
||||
|
@ -92,9 +92,9 @@ export const SidePanel: Story = {
|
|||
children: <Routes />,
|
||||
items: {
|
||||
...items,
|
||||
top: undefined
|
||||
}
|
||||
}
|
||||
top: undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const JustContent: Story = {
|
||||
|
@ -104,6 +104,6 @@ export const JustContent: Story = {
|
|||
Hello <Routes />
|
||||
</>
|
||||
),
|
||||
items: {}
|
||||
}
|
||||
items: {},
|
||||
},
|
||||
};
|
|
@ -1,132 +0,0 @@
|
|||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { Button } from '@procyon/components/Button';
|
||||
import { Modals } from '@procyon/components/Modal';
|
||||
import { AddModalButton } from '@procyon/components/Modal/components/AddModalButton';
|
||||
import { ModalWrapper, openModal as openOutsideReactModal } from '@procyon/components/Modal/context';
|
||||
import { useModal } from '@procyon/components/Modal/useModal';
|
||||
import { TextField } from '@procyon/components/TextField';
|
||||
|
||||
type Story = StoryObj<typeof Modals>;
|
||||
|
||||
export default {
|
||||
component: Modals,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div className="h-80">
|
||||
<ModalWrapper>
|
||||
<Story />
|
||||
<Modals />
|
||||
</ModalWrapper>
|
||||
</div>
|
||||
),
|
||||
],
|
||||
} as Meta;
|
||||
|
||||
export const Default: Story = {
|
||||
name: 'Default',
|
||||
render: () => {
|
||||
return (
|
||||
<AddModalButton
|
||||
id="default"
|
||||
title="Open modal"
|
||||
modalTitle="Default modal"
|
||||
Component={() => <div>Hi there</div>}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const Hook: Story = {
|
||||
name: 'Hook',
|
||||
render: () => {
|
||||
const Comp = () => {
|
||||
const [openModal] = useModal('modal2', {
|
||||
Component: () => <div>Hi there</div>,
|
||||
title: 'Modal Title',
|
||||
});
|
||||
return <Button label="Open Modal" onClick={openModal} />;
|
||||
};
|
||||
return <Comp />;
|
||||
},
|
||||
};
|
||||
|
||||
export const OutsideReact: Story = {
|
||||
name: 'Function',
|
||||
render: () => {
|
||||
return (
|
||||
<Button
|
||||
label="Open Modal"
|
||||
onClick={() =>
|
||||
openOutsideReactModal({
|
||||
Component: () => <div>Hi there</div>,
|
||||
id: 'modal3',
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const Small: Story = {
|
||||
name: 'Small',
|
||||
render: () => {
|
||||
return (
|
||||
<AddModalButton
|
||||
id="small"
|
||||
title="Open modal"
|
||||
modalTitle="Small modal"
|
||||
Component={() => (
|
||||
<div>
|
||||
<TextField name="search" label="Search" />
|
||||
</div>
|
||||
)}
|
||||
style={{
|
||||
width: 250,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const Multiple: Story = {
|
||||
name: 'Multiple',
|
||||
render: () => {
|
||||
return (
|
||||
<AddModalButton
|
||||
id="multiple"
|
||||
title="Open modal"
|
||||
modalTitle="First modal"
|
||||
Component={() => (
|
||||
<div>
|
||||
Hello
|
||||
<AddModalButton
|
||||
Component={() => <div>Hi there, again</div>}
|
||||
title="Open next modal"
|
||||
modalTitle="Second modal"
|
||||
id="secondModal"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCloseCallback: Story = {
|
||||
render: () => {
|
||||
return (
|
||||
<AddModalButton
|
||||
id="withCloseCallback"
|
||||
title="Open modal"
|
||||
modalTitle="Modal"
|
||||
Component={() => <div>Hello</div>}
|
||||
props={{
|
||||
onClose: () => action('Close callback called.'),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
|
@ -1,69 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { Modals } from '@procyon/components/Modal/components/Modals';
|
||||
import { ModalWrapper } from '@procyon/components/Modal/context';
|
||||
import { Selectbox } from '@procyon/components/Selectbox';
|
||||
|
||||
type Story = StoryObj<typeof Selectbox>;
|
||||
|
||||
const description = `
|
||||
\`import Selectbox from '@procyon/components/Selectbox';\`
|
||||
|
||||
You need to wrap this component with \`ModalWrapper\` component.
|
||||
`;
|
||||
|
||||
export default {
|
||||
component: Selectbox,
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: description,
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ height: '35rem' }}>
|
||||
<ModalWrapper>
|
||||
<Story />
|
||||
<Modals />
|
||||
</ModalWrapper>
|
||||
</div>
|
||||
),
|
||||
],
|
||||
} as Meta;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
name: 'selectbox',
|
||||
label: 'Zvolte den v týdnu',
|
||||
options: [
|
||||
{ name: 'Pondělí', code: 'po' },
|
||||
{ name: 'Úterý', code: 'ut' },
|
||||
{ name: 'Středa', code: 'st' },
|
||||
{ name: 'Čtvrtek', code: 'ct' },
|
||||
{ name: 'Pátek', code: 'pa' },
|
||||
{ name: 'Sobota', code: 'so' },
|
||||
{ name: 'Neděle', code: 'ne' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const Atocomplete: Story = {
|
||||
args: {
|
||||
autoComplete: true,
|
||||
name: 'selectboxAutoComplete',
|
||||
label: 'Zvolte den v týdnu',
|
||||
options: [
|
||||
{ name: 'Pondělí', code: 'po' },
|
||||
{ name: 'Úterý', code: 'ut' },
|
||||
{ name: 'Středa', code: 'st' },
|
||||
{ name: 'Čtvrtek', code: 'ct' },
|
||||
{ name: 'Pátek', code: 'pa' },
|
||||
{ name: 'Sobota', code: 'so' },
|
||||
{ name: 'Neděle', code: 'ne' },
|
||||
],
|
||||
},
|
||||
};
|
|
@ -29,7 +29,7 @@ import { TrashIcon } from '@procyon/components/Icons/Trash';
|
|||
import { UserIcon } from '@procyon/components/Icons/User';
|
||||
import { UserTieIcon } from '@procyon/components/Icons/UserTie';
|
||||
|
||||
<Meta title="components/Iconography" />
|
||||
<Meta />
|
||||
|
||||
# Iconography
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
import React from 'react';
|
||||
import { Skeleton } from 'packages/components/src/Skeleton';
|
||||
import { boolean, object, ObjectSchema, string } from 'yup';
|
||||
|
||||
import { Placeholder } from '@procyon/api/components/Placeholder';
|
||||
import { FieldPlaceholder } from '@procyon/api/components/Placeholder/shapes/Field';
|
||||
import { Button } from '@procyon/components/Button';
|
||||
import { Grid, GridSizeEnum } from '@procyon/components/Grid';
|
||||
import { GridCol } from '@procyon/components/GridCol';
|
||||
import { Grid, GridSizeEnum } from '@procyon/components/Grid/components/Grid';
|
||||
import { GridCol } from '@procyon/components/Grid/components/GridCol';
|
||||
import { BuildingStoreIcon } from '@procyon/components/Icons/BuildingStore';
|
||||
import { Field } from '@procyon/forms/Field';
|
||||
import { Form } from '@procyon/forms/Form';
|
||||
|
@ -65,9 +64,9 @@ const ExampleForm = () => {
|
|||
<Grid cols={{ [md]: 3 }}>
|
||||
<GridCol colSpan={{ [md]: 3 }}>
|
||||
<Field component="TEXT" name="text" label="Input" />
|
||||
<Placeholder active={formState.isSubmitted} component={<FieldPlaceholder />}>
|
||||
<Field component="TEXT" name="textIcon" label={<BuildingStoreIcon />} />
|
||||
</Placeholder>
|
||||
{/*<Skeleton active={formState.isSubmitted} component={<FieldPlaceholder />}>*/}
|
||||
<Field component="TEXT" name="textIcon" label={<BuildingStoreIcon />} />
|
||||
{/*</Skeleton>*/}
|
||||
<Field
|
||||
component="CHECK"
|
||||
name="checkbox"
|
||||
|
|
|
@ -45,9 +45,6 @@
|
|||
"@procyon/localization/*": [
|
||||
"./packages/localization/src/*"
|
||||
],
|
||||
"@procyon/router/*": [
|
||||
"./packages/router/src/*"
|
||||
],
|
||||
"@procyon/styles/*": [
|
||||
"./packages/styles/src/*"
|
||||
],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue