Remove unused code and refactor components for React Native
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:
Roman Jaroš 2024-02-20 15:22:13 +00:00
parent 834f2d3bba
commit 9e0aec7186
90 changed files with 718 additions and 686 deletions

6
.storybook/manager.ts Normal file
View file

@ -0,0 +1,6 @@
import { addons } from '@storybook/manager-api';
addons.setConfig({
panelPosition: 'bottom',
showToolbar: true,
});

View file

@ -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',

View file

@ -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'),

View file

@ -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',

View file

@ -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",

View file

@ -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;

View file

@ -1 +0,0 @@
export { Placeholder, type PlaceholderProps } from './Placeholder';

View file

@ -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>
);
};

View file

@ -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>
);
};

View file

@ -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>
</>
);
};

View file

@ -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({

View file

@ -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;
}
}, []);
};

View file

@ -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>;

View file

@ -150,7 +150,7 @@ const stylesSheet = createStyleSheet((theme) => ({
};
return {
fontFamily: theme.common.fontFamily,
fontFamily: theme.fontFamily,
variants: {
size: {
[SizeEnum.sm]: variantForSize(SizeEnum.sm),

View file

@ -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: {

View file

@ -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" />}

View file

@ -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';

View file

@ -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),

View file

@ -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(

View file

@ -0,0 +1,2 @@
export { Grid, GridSizeEnum, GridVerticalAlignEnum } from './components/Grid';
export { GridCol } from './components/GridCol';

View file

@ -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: {

View file

@ -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: {

View file

@ -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,
},
};

View file

@ -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} />;
};

View file

@ -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: {},
}));

View file

@ -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;
};

View file

@ -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 })}

View file

@ -0,0 +1 @@
export { Navigator, NavigatorProps } from './components/Navigator';

View file

@ -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,

View file

@ -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>
);
};

View file

@ -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} />
}
/>
);
};

View file

@ -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 }}>

View 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>
);
};

View file

@ -1 +0,0 @@
export { Skeleton, SkeletonProps } from './components/Skeleton';

View file

@ -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),

View file

@ -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" />}
/>

View file

@ -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

View file

@ -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,
},
},
},
},
}));

View file

@ -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>;

View file

@ -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"
}
}

View file

@ -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;
};

View file

@ -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;
};

View file

@ -1,19 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"composite": true,
"rootDir": "./src"
},
"include": [
"./src"
],
"exclude": [
"**/__tests__/**/*",
],
"references": [
{
"path": "../types/tsconfig.build.json"
}
],
}

View file

@ -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',

View file

@ -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;

View file

@ -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;
}

View file

@ -1,4 +0,0 @@
export type Action<P, T = string> = {
readonly payload?: P;
readonly type: T;
};

View file

@ -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 };

View file

@ -1,5 +0,0 @@
export const logVal = (x: any) => {
// eslint-disable-next-line no-console
console.log(x);
return x;
};

View file

@ -1,3 +0,0 @@
export const noop = (): void => {
/* function is empty */
};

9
pnpm-lock.yaml generated
View file

@ -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':

View file

@ -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 />,
},
};

View file

@ -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>
</>
);
};

View file

@ -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);

View file

@ -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>;

View file

@ -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',
},

View 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 },
],
},
};

View file

@ -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} />
</>
),
},
};

View 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' },
],
},
};

View file

@ -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>&nbsp;</ColoredDiv>
}
children: <ColoredDiv>&nbsp;</ColoredDiv>,
},
} as Meta<typeof Grid>;
const { sm, lg } = GridSizeEnum;
@ -109,7 +113,7 @@ export const Default: Story = {
<ColoredDiv>&nbsp;</ColoredDiv>
</GridCol>
</Grid>
)
),
};
export const Left: Story = {
@ -119,7 +123,7 @@ export const Left: Story = {
<ColoredDiv>&nbsp;</ColoredDiv>
</GridCol>
</Grid>
)
),
};
export const Center: Story = {
@ -129,7 +133,7 @@ export const Center: Story = {
<ColoredDiv>&nbsp;</ColoredDiv>
</GridCol>
</Grid>
)
),
};
export const Right: Story = {
@ -139,7 +143,7 @@ export const Right: Story = {
<ColoredDiv>&nbsp;</ColoredDiv>
</GridCol>
</Grid>
)
),
};
export const Top: Story = {
@ -159,7 +163,7 @@ export const Top: Story = {
<ColoredDiv>&nbsp;</ColoredDiv>
</GridCol>
</Grid>
)
),
};
export const Middle: Story = {
@ -179,7 +183,7 @@ export const Middle: Story = {
<ColoredDiv>&nbsp;</ColoredDiv>
</GridCol>
</Grid>
)
),
};
export const Bottom: Story = {
@ -199,7 +203,7 @@ export const Bottom: Story = {
<ColoredDiv>&nbsp;</ColoredDiv>
</GridCol>
</Grid>
)
),
};
export const Responsive: Story = {
@ -219,5 +223,5 @@ export const Responsive: Story = {
<ColoredDiv>&nbsp;</ColoredDiv>
</GridCol>
</Grid>
)
),
};

View file

@ -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',
});

View 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',
})
}
/>
);
},
};

View file

@ -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: {},
},
};

View file

@ -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.'),
}}
/>
);
},
};

View file

@ -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' },
],
},
};

View file

@ -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

View file

@ -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"

View file

@ -45,9 +45,6 @@
"@procyon/localization/*": [
"./packages/localization/src/*"
],
"@procyon/router/*": [
"./packages/router/src/*"
],
"@procyon/styles/*": [
"./packages/styles/src/*"
],