@ainias42/react-bootstrap-mobile 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +189 -0
- package/.prettierrc +5 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/babel.config.js +22 -0
- package/bin/build.js +60 -0
- package/bin/release.sh +35 -0
- package/bin/updateCopies.js +86 -0
- package/bootstrapReactMobile.ts +87 -0
- package/dist/bootstrapReactMobile.d.ts +87 -0
- package/dist/bootstrapReactMobile.js +6275 -0
- package/dist/src/Components/ActionSheet/ActionSheet.d.ts +21 -0
- package/dist/src/Components/Card/Card.d.ts +13 -0
- package/dist/src/Components/Clickable/Clickable.d.ts +14 -0
- package/dist/src/Components/Dialog/AlertDialog.d.ts +10 -0
- package/dist/src/Components/Dialog/ButtonDialog.d.ts +13 -0
- package/dist/src/Components/Dialog/ConfirmDialog.d.ts +11 -0
- package/dist/src/Components/Dialog/Dialog.d.ts +14 -0
- package/dist/src/Components/Dialog/DialogBackground.d.ts +7 -0
- package/dist/src/Components/Dialog/DialogContainer.d.ts +6 -0
- package/dist/src/Components/Dialog/DialogContext.d.ts +9 -0
- package/dist/src/Components/Dialog/useAlertDialog.d.ts +1 -0
- package/dist/src/Components/Dialog/useConfirmDialog.d.ts +1 -0
- package/dist/src/Components/DragAndDrop/DragItem.d.ts +7 -0
- package/dist/src/Components/DragAndDrop/DropArea.d.ts +7 -0
- package/dist/src/Components/DragAndDrop/useStrictEnabled.d.ts +1 -0
- package/dist/src/Components/FormElements/Button/Button.d.ts +8 -0
- package/dist/src/Components/FormElements/CheckBox/Checkbox.d.ts +10 -0
- package/dist/src/Components/FormElements/ColorInput/ColorInput.d.ts +17 -0
- package/dist/src/Components/FormElements/ColorInput/sharedSelectedColor.d.ts +4 -0
- package/dist/src/Components/FormElements/ImageInput/ImageInput.d.ts +17 -0
- package/dist/src/Components/FormElements/Input/HiddenInput.d.ts +8 -0
- package/dist/src/Components/FormElements/Input/Input.d.ts +10 -0
- package/dist/src/Components/FormElements/Input/PasswordInput/PasswordInput.d.ts +4 -0
- package/dist/src/Components/FormElements/SearchSelectInput/SearchSelectInput.d.ts +10 -0
- package/dist/src/Components/FormElements/Select/Select.d.ts +16 -0
- package/dist/src/Components/FormElements/Slider/Slider.d.ts +8 -0
- package/dist/src/Components/FormElements/Switch/Switch.d.ts +12 -0
- package/dist/src/Components/FormElements/Textarea/Textarea.d.ts +12 -0
- package/dist/src/Components/FormElements/hooks/useOnChangeDone.d.ts +2 -0
- package/dist/src/Components/FullScreen/FullScreen.d.ts +14 -0
- package/dist/src/Components/Hooks/useBreakpoint.d.ts +11 -0
- package/dist/src/Components/Hooks/useComposedRef.d.ts +2 -0
- package/dist/src/Components/Hooks/useDebounced.d.ts +1 -0
- package/dist/src/Components/Hooks/useDelayed.d.ts +1 -0
- package/dist/src/Components/Hooks/useInViewport.d.ts +2 -0
- package/dist/src/Components/Hooks/useKeyListener.d.ts +3 -0
- package/dist/src/Components/Hooks/useListener.d.ts +18 -0
- package/dist/src/Components/Hooks/useOnMount.d.ts +1 -0
- package/dist/src/Components/Hooks/useOnce.d.ts +1 -0
- package/dist/src/Components/Icon/Icon.d.ts +13 -0
- package/dist/src/Components/Image/Image.d.ts +12 -0
- package/dist/src/Components/InViewport/InViewport.d.ts +11 -0
- package/dist/src/Components/Layout/Block.d.ts +7 -0
- package/dist/src/Components/Layout/Container.d.ts +15 -0
- package/dist/src/Components/Layout/Flex.d.ts +10 -0
- package/dist/src/Components/Layout/Grid/Grid.d.ts +9 -0
- package/dist/src/Components/Layout/Grid/GridItem.d.ts +25 -0
- package/dist/src/Components/Layout/Grow.d.ts +9 -0
- package/dist/src/Components/Layout/Inline.d.ts +7 -0
- package/dist/src/Components/Layout/InlineBlock.d.ts +7 -0
- package/dist/src/Components/Layout/View.d.ts +9 -0
- package/dist/src/Components/Layout/ViewWithoutListeners.d.ts +8 -0
- package/dist/src/Components/List/BulletList/BulletList.d.ts +8 -0
- package/dist/src/Components/List/BulletList/ListItem.d.ts +7 -0
- package/dist/src/Components/List/List.d.ts +10 -0
- package/dist/src/Components/LoadingArea/LoadingArea.d.ts +12 -0
- package/dist/src/Components/LoadingCircle/LoadingCircle.d.ts +8 -0
- package/dist/src/Components/Menu/Menu.d.ts +16 -0
- package/dist/src/Components/Menu/useMenu.d.ts +3 -0
- package/dist/src/Components/RbmComponentProps.d.ts +31 -0
- package/dist/src/Components/SizeCalculator/SizeCalculator.d.ts +9 -0
- package/dist/src/Components/SpoilerList/Spoiler/Spoiler.d.ts +15 -0
- package/dist/src/Components/SpoilerList/SpoilerList.d.ts +15 -0
- package/dist/src/Components/SpoilerList/useSpoilerGroup.d.ts +12 -0
- package/dist/src/Components/TabBar/TabBar.d.ts +30 -0
- package/dist/src/Components/TabBar/TabBarButton.d.ts +9 -0
- package/dist/src/Components/Table/Table.d.ts +36 -0
- package/dist/src/Components/Text/Heading.d.ts +7 -0
- package/dist/src/Components/Text/Text.d.ts +26 -0
- package/dist/src/Components/Toast/Toast.d.ts +13 -0
- package/dist/src/Components/Toast/ToastContainer.d.ts +7 -0
- package/dist/src/Components/TopBar/MoreButton.d.ts +9 -0
- package/dist/src/Components/TopBar/TopBar.d.ts +25 -0
- package/dist/src/Components/TopBar/TopBarButton.d.ts +9 -0
- package/dist/src/StyleProvider.d.ts +2 -0
- package/dist/src/TypeHelpers.d.ts +4 -0
- package/dist/src/WindowContext/WindowContext.d.ts +3 -0
- package/dist/src/WrongChildError.d.ts +4 -0
- package/dist/src/helper/Characters.d.ts +5 -0
- package/dist/src/helper/DistributiveOmit.d.ts +1 -0
- package/dist/src/helper/EmptyProps.d.ts +1 -0
- package/dist/src/helper/memoComparator.d.ts +1 -0
- package/dist/src/helper/nonEmptyString.d.ts +1 -0
- package/dist/src/helper/withForwardRef.d.ts +7 -0
- package/dist/src/helper/withMemo.d.ts +3 -0
- package/dist/src/helper/withRenderBrowserOnly.d.ts +2 -0
- package/dist/src/helper/withRestrictedChildren.d.ts +6 -0
- package/package.json +92 -0
- package/react-bootstrap-mobile.scss +6 -0
- package/scripts/getPackageJson.js +25 -0
- package/src/Components/ActionSheet/ActionSheet.tsx +115 -0
- package/src/Components/ActionSheet/actionSheet.scss +153 -0
- package/src/Components/Card/Card.tsx +46 -0
- package/src/Components/Card/card.scss +76 -0
- package/src/Components/Clickable/Clickable.tsx +174 -0
- package/src/Components/Clickable/clickable.scss +3 -0
- package/src/Components/Dialog/AlertDialog.tsx +44 -0
- package/src/Components/Dialog/ButtonDialog.tsx +57 -0
- package/src/Components/Dialog/ConfirmDialog.tsx +46 -0
- package/src/Components/Dialog/Dialog.tsx +82 -0
- package/src/Components/Dialog/DialogBackground.tsx +38 -0
- package/src/Components/Dialog/DialogContainer.tsx +77 -0
- package/src/Components/Dialog/DialogContext.ts +21 -0
- package/src/Components/Dialog/buttonDialog.scss +114 -0
- package/src/Components/Dialog/dialog.scss +30 -0
- package/src/Components/Dialog/dialogBackground.scss +4 -0
- package/src/Components/Dialog/useAlertDialog.ts +13 -0
- package/src/Components/Dialog/useConfirmDialog.ts +13 -0
- package/src/Components/DragAndDrop/DragItem.tsx +38 -0
- package/src/Components/DragAndDrop/DropArea.tsx +43 -0
- package/src/Components/DragAndDrop/useStrictEnabled.ts +20 -0
- package/src/Components/FormElements/Button/Button.tsx +25 -0
- package/src/Components/FormElements/Button/button.scss +39 -0
- package/src/Components/FormElements/CheckBox/Checkbox.tsx +61 -0
- package/src/Components/FormElements/CheckBox/checkbox.scss +107 -0
- package/src/Components/FormElements/ColorInput/ColorInput.tsx +139 -0
- package/src/Components/FormElements/ColorInput/colorInput.scss +35 -0
- package/src/Components/FormElements/ColorInput/sharedSelectedColor.ts +40 -0
- package/src/Components/FormElements/ImageInput/ImageInput.tsx +97 -0
- package/src/Components/FormElements/ImageInput/imageInput.scss +24 -0
- package/src/Components/FormElements/Input/HiddenInput.tsx +43 -0
- package/src/Components/FormElements/Input/Input.tsx +102 -0
- package/src/Components/FormElements/Input/PasswordInput/PasswordInput.tsx +55 -0
- package/src/Components/FormElements/Input/PasswordInput/passwordInput.scss +7 -0
- package/src/Components/FormElements/Input/input.scss +57 -0
- package/src/Components/FormElements/SearchSelectInput/SearchSelectInput.tsx +162 -0
- package/src/Components/FormElements/SearchSelectInput/seachSelectInput.scss +90 -0
- package/src/Components/FormElements/Select/Select.tsx +77 -0
- package/src/Components/FormElements/Select/select.scss +51 -0
- package/src/Components/FormElements/Slider/Slider.tsx +80 -0
- package/src/Components/FormElements/Slider/slider.scss +92 -0
- package/src/Components/FormElements/Switch/Switch.tsx +82 -0
- package/src/Components/FormElements/Switch/switch.scss +149 -0
- package/src/Components/FormElements/Textarea/Textarea.tsx +77 -0
- package/src/Components/FormElements/Textarea/textarea.scss +22 -0
- package/src/Components/FormElements/hooks/useOnChangeDone.ts +16 -0
- package/src/Components/FullScreen/FullScreen.tsx +89 -0
- package/src/Components/Hooks/useBreakpoint.ts +66 -0
- package/src/Components/Hooks/useComposedRef.ts +17 -0
- package/src/Components/Hooks/useDebounced.ts +22 -0
- package/src/Components/Hooks/useDelayed.ts +46 -0
- package/src/Components/Hooks/useInViewport.ts +23 -0
- package/src/Components/Hooks/useKeyListener.ts +77 -0
- package/src/Components/Hooks/useListener.ts +73 -0
- package/src/Components/Hooks/useOnMount.ts +12 -0
- package/src/Components/Hooks/useOnce.ts +11 -0
- package/src/Components/Icon/Icon.tsx +45 -0
- package/src/Components/Image/Image.tsx +44 -0
- package/src/Components/Image/image.scss +3 -0
- package/src/Components/InViewport/InViewport.tsx +71 -0
- package/src/Components/InViewport/inViewport.scss +3 -0
- package/src/Components/Layout/Block.tsx +48 -0
- package/src/Components/Layout/Container.tsx +57 -0
- package/src/Components/Layout/Flex.tsx +51 -0
- package/src/Components/Layout/Grid/Grid.tsx +53 -0
- package/src/Components/Layout/Grid/GridItem.tsx +138 -0
- package/src/Components/Layout/Grid/grid.scss +43 -0
- package/src/Components/Layout/Grow.tsx +51 -0
- package/src/Components/Layout/Inline.tsx +48 -0
- package/src/Components/Layout/InlineBlock.tsx +48 -0
- package/src/Components/Layout/View.tsx +40 -0
- package/src/Components/Layout/ViewWithoutListeners.tsx +40 -0
- package/src/Components/Layout/container.scss +12 -0
- package/src/Components/Layout/layout.scss +56 -0
- package/src/Components/List/BulletList/BulletList.tsx +33 -0
- package/src/Components/List/BulletList/ListItem.tsx +34 -0
- package/src/Components/List/List.tsx +88 -0
- package/src/Components/List/list.scss +30 -0
- package/src/Components/LoadingArea/LoadingArea.tsx +64 -0
- package/src/Components/LoadingArea/loadingArea.scss +19 -0
- package/src/Components/LoadingCircle/LoadingCircle.tsx +41 -0
- package/src/Components/LoadingCircle/loadingCircle.scss +42 -0
- package/src/Components/Menu/Menu.tsx +113 -0
- package/src/Components/Menu/menu.scss +21 -0
- package/src/Components/Menu/useMenu.ts +20 -0
- package/src/Components/RbmComponentProps.ts +40 -0
- package/src/Components/SizeCalculator/SizeCalculator.tsx +45 -0
- package/src/Components/SpoilerList/Spoiler/Spoiler.tsx +106 -0
- package/src/Components/SpoilerList/Spoiler/spoiler.scss +120 -0
- package/src/Components/SpoilerList/SpoilerList.tsx +63 -0
- package/src/Components/SpoilerList/useSpoilerGroup.ts +39 -0
- package/src/Components/TabBar/TabBar.tsx +117 -0
- package/src/Components/TabBar/TabBarButton.tsx +44 -0
- package/src/Components/TabBar/tabBar.scss +108 -0
- package/src/Components/Table/Table.tsx +182 -0
- package/src/Components/Text/Heading.tsx +44 -0
- package/src/Components/Text/Text.tsx +79 -0
- package/src/Components/Text/heading.scss +3 -0
- package/src/Components/Text/text.scss +60 -0
- package/src/Components/Toast/Toast.tsx +107 -0
- package/src/Components/Toast/ToastContainer.tsx +35 -0
- package/src/Components/Toast/toast.scss +52 -0
- package/src/Components/TopBar/MoreButton.tsx +38 -0
- package/src/Components/TopBar/TopBar.tsx +176 -0
- package/src/Components/TopBar/TopBarButton.tsx +29 -0
- package/src/Components/TopBar/topBar.scss +124 -0
- package/src/StyleProvider.ts +4 -0
- package/src/TypeHelpers.ts +4 -0
- package/src/WindowContext/WindowContext.ts +8 -0
- package/src/WrongChildError.ts +19 -0
- package/src/env.d.ts +1 -0
- package/src/helper/Characters.ts +5 -0
- package/src/helper/DistributiveOmit.ts +1 -0
- package/src/helper/EmptyProps.ts +2 -0
- package/src/helper/memoComparator.ts +18 -0
- package/src/helper/nonEmptyString.ts +8 -0
- package/src/helper/withForwardRef.ts +28 -0
- package/src/helper/withMemo.ts +16 -0
- package/src/helper/withRenderBrowserOnly.tsx +30 -0
- package/src/helper/withRestrictedChildren.tsx +57 -0
- package/src/scss/_animations.scss +46 -0
- package/src/scss/_baseClasses.scss +27 -0
- package/src/scss/_colors.scss +13 -0
- package/src/scss/_default.scss +17 -0
- package/src/scss/_designMixin.scss +13 -0
- package/src/scss/_mobileMixin.scss +35 -0
- package/src/scss/_variables.scss +22 -0
- package/src/types/isomorphic-style-loader.d.ts +3 -0
- package/src/types/react-table-config.d.ts +120 -0
- package/src/types/scss-module.d.ts +7 -0
- package/tsconfig.json +57 -0
- package/webpack.config.js +85 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useCallback, useState, MouseEvent } from 'react';
|
|
2
|
+
import { MenuProps } from './Menu';
|
|
3
|
+
|
|
4
|
+
export function useMenu() {
|
|
5
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
6
|
+
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
7
|
+
const open = useCallback((e: MouseEvent) => {
|
|
8
|
+
setPosition({ x: e.clientX, y: e.clientY });
|
|
9
|
+
setIsOpen(true);
|
|
10
|
+
}, []);
|
|
11
|
+
|
|
12
|
+
const props: Omit<MenuProps, 'items'> = {
|
|
13
|
+
isOpen,
|
|
14
|
+
x: position.x,
|
|
15
|
+
y: position.y,
|
|
16
|
+
onClose: useCallback(() => setIsOpen(false), []),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return [open, props] as const;
|
|
20
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import { Recursive } from '../TypeHelpers';
|
|
3
|
+
|
|
4
|
+
type Child = Recursive<JSX.Element | undefined | null | Child[]> | false;
|
|
5
|
+
export type WithNoStringProps =
|
|
6
|
+
| {
|
|
7
|
+
children?: Child;
|
|
8
|
+
__allowChildren?: 'html';
|
|
9
|
+
}
|
|
10
|
+
| {
|
|
11
|
+
children?: ReactNode;
|
|
12
|
+
__allowChildren: 'text' | 'all';
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type WithNoStringAndChildrenProps =
|
|
16
|
+
| {
|
|
17
|
+
children: Child;
|
|
18
|
+
__allowChildren?: 'html';
|
|
19
|
+
}
|
|
20
|
+
| {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
__allowChildren: 'text' | 'all';
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type WithStringProps = {
|
|
26
|
+
children?: Recursive<string>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type WithStringAndChildrenProps = {
|
|
30
|
+
children: Recursive<string>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type WithNoChildren = {
|
|
34
|
+
children?: never;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type RbmComponentProps<SpecialProps, ChildrenProps = WithNoStringProps> = ChildrenProps & {
|
|
38
|
+
className?: string;
|
|
39
|
+
style?: CSSProperties;
|
|
40
|
+
} & SpecialProps;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { withMemo } from '../../helper/withMemo';
|
|
3
|
+
import { WithNoStringAndChildrenProps } from '../RbmComponentProps';
|
|
4
|
+
import { InlineBlock } from '../Layout/InlineBlock';
|
|
5
|
+
import { useEffect, useRef } from 'react';
|
|
6
|
+
|
|
7
|
+
export type SizeCalculatorProps = {
|
|
8
|
+
onSize: (width: number, height: number) => void;
|
|
9
|
+
absolute?: boolean;
|
|
10
|
+
} & WithNoStringAndChildrenProps;
|
|
11
|
+
|
|
12
|
+
function SizeCalculator({ onSize, children, absolute = false }: SizeCalculatorProps) {
|
|
13
|
+
// Variables
|
|
14
|
+
|
|
15
|
+
// Refs
|
|
16
|
+
const ref = useRef<HTMLSpanElement>(null);
|
|
17
|
+
|
|
18
|
+
// States
|
|
19
|
+
|
|
20
|
+
// Selectors
|
|
21
|
+
|
|
22
|
+
// Callbacks
|
|
23
|
+
|
|
24
|
+
// Effects
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!ref.current) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
onSize(ref.current.clientWidth, ref.current.clientHeight);
|
|
30
|
+
}, [onSize]);
|
|
31
|
+
|
|
32
|
+
// Other
|
|
33
|
+
|
|
34
|
+
// Render Functions
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<InlineBlock ref={ref} __allowChildren="all" style={{ position: absolute ? 'absolute' : 'static' }}>
|
|
38
|
+
{children}
|
|
39
|
+
</InlineBlock>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Need SizeCalculatorMemo for autocompletion of phpstorm
|
|
44
|
+
const SizeCalculatorMemo = withMemo(SizeCalculator);
|
|
45
|
+
export { SizeCalculatorMemo as SizeCalculator };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { withMemo } from '../../../helper/withMemo';
|
|
3
|
+
import { RbmComponentProps } from '../../RbmComponentProps';
|
|
4
|
+
import { ReactChild, useCallback, useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { Flex } from '../../Layout/Flex';
|
|
6
|
+
import { Grow } from '../../Layout/Grow';
|
|
7
|
+
import { Text, TEXT_SIZE } from '../../Text/Text';
|
|
8
|
+
import { Block } from '../../Layout/Block';
|
|
9
|
+
import { Clickable } from '../../Clickable/Clickable';
|
|
10
|
+
|
|
11
|
+
import styles from './spoiler.scss';
|
|
12
|
+
import classNames from 'classnames';
|
|
13
|
+
import { OptionalListener, useListener } from '../../Hooks/useListener';
|
|
14
|
+
import { Icon } from '../../Icon/Icon';
|
|
15
|
+
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';
|
|
16
|
+
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
|
17
|
+
|
|
18
|
+
export type SpoilerProps<OnClickData> = RbmComponentProps<
|
|
19
|
+
{
|
|
20
|
+
title: ReactChild;
|
|
21
|
+
initialOpen?: boolean;
|
|
22
|
+
open?: boolean;
|
|
23
|
+
noClosingAnimation?: boolean;
|
|
24
|
+
openIcon?: IconProp | null;
|
|
25
|
+
closeIcon?: IconProp | null;
|
|
26
|
+
} & OptionalListener<'onClick', OnClickData>
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
function Spoiler<OnClickData>({
|
|
30
|
+
title,
|
|
31
|
+
children,
|
|
32
|
+
initialOpen = false,
|
|
33
|
+
noClosingAnimation = false,
|
|
34
|
+
openIcon = faChevronDown,
|
|
35
|
+
closeIcon = faChevronUp,
|
|
36
|
+
className,
|
|
37
|
+
style,
|
|
38
|
+
open,
|
|
39
|
+
...listenerProps
|
|
40
|
+
}: SpoilerProps<OnClickData>) {
|
|
41
|
+
// Variables
|
|
42
|
+
|
|
43
|
+
// Refs
|
|
44
|
+
|
|
45
|
+
// States
|
|
46
|
+
const [isOpen, setIsOpen] = useState(open ?? initialOpen);
|
|
47
|
+
const [isInitialValue, setIsInitialValue] = useState(true);
|
|
48
|
+
const savedOpen = useRef(open);
|
|
49
|
+
|
|
50
|
+
// Selectors
|
|
51
|
+
|
|
52
|
+
// Callbacks
|
|
53
|
+
const onClickListener = useListener<'onClick', OnClickData, boolean>('onClick', listenerProps);
|
|
54
|
+
|
|
55
|
+
const toggleOpen = useCallback(() => {
|
|
56
|
+
if (open !== undefined) {
|
|
57
|
+
onClickListener?.(!open);
|
|
58
|
+
} else {
|
|
59
|
+
setIsInitialValue(false);
|
|
60
|
+
setIsOpen((old) => {
|
|
61
|
+
onClickListener?.(!old);
|
|
62
|
+
return !old;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}, [onClickListener, open]);
|
|
66
|
+
|
|
67
|
+
// Effects
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (savedOpen.current !== open) {
|
|
70
|
+
setIsInitialValue(false);
|
|
71
|
+
}
|
|
72
|
+
}, [open]);
|
|
73
|
+
|
|
74
|
+
// Other
|
|
75
|
+
const titleComponent =
|
|
76
|
+
typeof title === 'string' || typeof title === 'number' ? <Text size={TEXT_SIZE.large}>{title}</Text> : title;
|
|
77
|
+
|
|
78
|
+
// Render Functions
|
|
79
|
+
const icon = open ?? isOpen ? closeIcon : openIcon;
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Clickable
|
|
83
|
+
onClick={toggleOpen}
|
|
84
|
+
className={classNames(className, styles.spoiler, {
|
|
85
|
+
[styles.open]: open ?? isOpen,
|
|
86
|
+
[styles.noAnimation]: isInitialValue,
|
|
87
|
+
[styles.noClosingAnimation]: noClosingAnimation,
|
|
88
|
+
})}
|
|
89
|
+
style={style}
|
|
90
|
+
>
|
|
91
|
+
<Flex horizontal={true}>
|
|
92
|
+
<Grow>{titleComponent}</Grow>
|
|
93
|
+
{icon ? <Icon icon={icon} className={styles.icon} /> : null}
|
|
94
|
+
</Flex>
|
|
95
|
+
<Block className={styles.bodyContainer}>
|
|
96
|
+
<Block __allowChildren="all" className={styles.body}>
|
|
97
|
+
{children}
|
|
98
|
+
</Block>
|
|
99
|
+
</Block>
|
|
100
|
+
</Clickable>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Need SpoilerMemo for autocompletion of phpstorm
|
|
105
|
+
const SpoilerMemo = withMemo(Spoiler, styles);
|
|
106
|
+
export { SpoilerMemo as Spoiler };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
@keyframes spoilerContainerOpen {
|
|
2
|
+
0% {
|
|
3
|
+
height: 0;
|
|
4
|
+
}
|
|
5
|
+
1% {
|
|
6
|
+
height: initial;
|
|
7
|
+
}
|
|
8
|
+
100% {
|
|
9
|
+
height: initial;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@keyframes spoilerOpen {
|
|
14
|
+
0% {
|
|
15
|
+
height: 0;
|
|
16
|
+
transform: translateY(-100%);
|
|
17
|
+
opacity: 0;
|
|
18
|
+
}
|
|
19
|
+
1% {
|
|
20
|
+
height: initial;
|
|
21
|
+
transform: translateY(-100%);
|
|
22
|
+
opacity: 0;
|
|
23
|
+
}
|
|
24
|
+
100% {
|
|
25
|
+
height: initial;
|
|
26
|
+
transform: translateY(0%);
|
|
27
|
+
opacity: 1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@keyframes spoilerContainerClose {
|
|
32
|
+
0% {
|
|
33
|
+
height: initial;
|
|
34
|
+
}
|
|
35
|
+
99% {
|
|
36
|
+
height: initial;
|
|
37
|
+
}
|
|
38
|
+
100% {
|
|
39
|
+
height: 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@keyframes spoilerClose {
|
|
44
|
+
0% {
|
|
45
|
+
height: initial;
|
|
46
|
+
transform: translateY(0%);
|
|
47
|
+
opacity: 1;
|
|
48
|
+
}
|
|
49
|
+
99% {
|
|
50
|
+
height: initial;
|
|
51
|
+
transform: translateY(-100%);
|
|
52
|
+
opacity: 0;
|
|
53
|
+
}
|
|
54
|
+
100% {
|
|
55
|
+
height: 0;
|
|
56
|
+
transform: translateY(-100%);
|
|
57
|
+
opacity: 0;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.spoiler {
|
|
62
|
+
display: block;
|
|
63
|
+
border-top: 1px solid var(--border-strong);
|
|
64
|
+
border-bottom: 1px solid var(--border-strong);
|
|
65
|
+
|
|
66
|
+
.icon {
|
|
67
|
+
margin-right: 0.5rem;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&.open, &.open.noClosingAnimation {
|
|
71
|
+
.bodyContainer {
|
|
72
|
+
height: initial;
|
|
73
|
+
animation-name: spoilerContainerOpen;
|
|
74
|
+
|
|
75
|
+
.body {
|
|
76
|
+
height: initial;
|
|
77
|
+
transform: translateY(0%);
|
|
78
|
+
animation-name: spoilerOpen;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
&.noAnimation, &.noAnimation.open {
|
|
84
|
+
.bodyContainer {
|
|
85
|
+
animation: none;
|
|
86
|
+
|
|
87
|
+
.body {
|
|
88
|
+
animation: none;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
&.noClosingAnimation {
|
|
94
|
+
.bodyContainer {
|
|
95
|
+
animation-name: none;
|
|
96
|
+
|
|
97
|
+
.body {
|
|
98
|
+
animation-name: none;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.bodyContainer {
|
|
104
|
+
height: 0;
|
|
105
|
+
overflow: hidden;
|
|
106
|
+
animation-name: spoilerContainerClose;
|
|
107
|
+
animation-duration: 0.7s;
|
|
108
|
+
animation-fill-mode: forwards;
|
|
109
|
+
|
|
110
|
+
.body {
|
|
111
|
+
height: 0;
|
|
112
|
+
overflow: hidden;
|
|
113
|
+
transform-origin: left top;
|
|
114
|
+
transform: translateY(-100%);
|
|
115
|
+
animation-name: spoilerClose;
|
|
116
|
+
animation-duration: 0.7s;
|
|
117
|
+
animation-fill-mode: forwards;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { withMemo } from '../../helper/withMemo';
|
|
3
|
+
import { ReactElement } from 'react';
|
|
4
|
+
import { RbmComponentProps, WithNoChildren } from '../RbmComponentProps';
|
|
5
|
+
import { Block } from '../Layout/Block';
|
|
6
|
+
import classNames from 'classnames';
|
|
7
|
+
import { Spoiler } from './Spoiler/Spoiler';
|
|
8
|
+
import { useSpoilerGroup } from './useSpoilerGroup';
|
|
9
|
+
|
|
10
|
+
export type SpoilerItem<BodyData, TitleData = string> = {
|
|
11
|
+
title: TitleData;
|
|
12
|
+
body: BodyData;
|
|
13
|
+
key: number | string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type SpoilerListProps<BodyData, TitleData = string> = RbmComponentProps<
|
|
17
|
+
{
|
|
18
|
+
data: SpoilerItem<BodyData, TitleData>[];
|
|
19
|
+
renderBody: (item: SpoilerItem<BodyData, TitleData>) => ReactElement;
|
|
20
|
+
renderTitle?: (item: SpoilerItem<BodyData, TitleData>) => ReactElement | string;
|
|
21
|
+
},
|
|
22
|
+
WithNoChildren
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
const stringIdentity = (data: any) => data as string;
|
|
26
|
+
|
|
27
|
+
function SpoilerList<BodyData, TitleData = string>({
|
|
28
|
+
data,
|
|
29
|
+
renderBody,
|
|
30
|
+
renderTitle = stringIdentity,
|
|
31
|
+
className,
|
|
32
|
+
style,
|
|
33
|
+
}: SpoilerListProps<BodyData, TitleData>) {
|
|
34
|
+
// Variables
|
|
35
|
+
const [propsGenerator] = useSpoilerGroup();
|
|
36
|
+
|
|
37
|
+
// Refs
|
|
38
|
+
|
|
39
|
+
// States
|
|
40
|
+
|
|
41
|
+
// Selectors
|
|
42
|
+
|
|
43
|
+
// Callbacks
|
|
44
|
+
|
|
45
|
+
// Effects
|
|
46
|
+
|
|
47
|
+
// Other
|
|
48
|
+
|
|
49
|
+
// Render Functions
|
|
50
|
+
return (
|
|
51
|
+
<Block className={classNames(className)} style={style}>
|
|
52
|
+
{data.map((item) => (
|
|
53
|
+
<Spoiler title={renderTitle(item)} {...propsGenerator(item.key)}>
|
|
54
|
+
{renderBody(item)}
|
|
55
|
+
</Spoiler>
|
|
56
|
+
))}
|
|
57
|
+
</Block>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Need SpoilerListMemo for autocompletion of phpstorm
|
|
62
|
+
const SpoilerListMemo = withMemo(SpoilerList);
|
|
63
|
+
export { SpoilerListMemo as SpoilerList };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export type SpoilerGroupOptions = {
|
|
4
|
+
closeWithClick: boolean;
|
|
5
|
+
noClosingAnimation: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function useSpoilerGroup(defaultKey?: number | string, options: Partial<SpoilerGroupOptions> = {}) {
|
|
9
|
+
const [openKey, setOpenKey] = useState<number | string | undefined>(defaultKey);
|
|
10
|
+
|
|
11
|
+
const onClick = useCallback(
|
|
12
|
+
(isOpen: boolean, key: string | number) => {
|
|
13
|
+
setOpenKey((oldKey) => {
|
|
14
|
+
if (options?.closeWithClick && oldKey === key && !isOpen) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
if (isOpen) {
|
|
18
|
+
return key;
|
|
19
|
+
}
|
|
20
|
+
return oldKey;
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
[options?.closeWithClick]
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const createProps = useCallback(
|
|
27
|
+
(key: string | number) => ({
|
|
28
|
+
onClick,
|
|
29
|
+
onClickData: key,
|
|
30
|
+
key,
|
|
31
|
+
open: key === openKey,
|
|
32
|
+
noClosingAnimation: options?.noClosingAnimation !== false,
|
|
33
|
+
closeIcon: null,
|
|
34
|
+
}),
|
|
35
|
+
[onClick, openKey, options?.noClosingAnimation]
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return [createProps, openKey] as const;
|
|
39
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { RbmComponentProps } from '../RbmComponentProps';
|
|
3
|
+
import { Icon, IconSource } from '../Icon/Icon';
|
|
4
|
+
import { ComponentType, useCallback, useState } from 'react';
|
|
5
|
+
import { Container } from '../Layout/Container';
|
|
6
|
+
import { TabBarButton } from './TabBarButton';
|
|
7
|
+
|
|
8
|
+
import styles from './tabBar.scss';
|
|
9
|
+
import { withMemo } from '../../helper/withMemo';
|
|
10
|
+
import classNames from 'classnames';
|
|
11
|
+
import { ListenerWithData } from '../Hooks/useListener';
|
|
12
|
+
import { Inline } from '../Layout/Inline';
|
|
13
|
+
import { Text } from '../Text/Text';
|
|
14
|
+
|
|
15
|
+
export type TabBarComponentButtonType = {
|
|
16
|
+
component: ComponentType<Record<string, any>>;
|
|
17
|
+
key?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type TabBarTitleButtonType = {
|
|
21
|
+
title: string;
|
|
22
|
+
icon?: IconSource;
|
|
23
|
+
key?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type TabBarIconButtonType = {
|
|
27
|
+
title?: string;
|
|
28
|
+
icon: IconSource;
|
|
29
|
+
key?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type TabBarButtonType = TabBarComponentButtonType | TabBarTitleButtonType | TabBarIconButtonType;
|
|
33
|
+
|
|
34
|
+
export type TabBarProps = RbmComponentProps<{
|
|
35
|
+
buttons: TabBarButtonType[];
|
|
36
|
+
startActiveTab?: number;
|
|
37
|
+
onTabChange: (newActiveTab: number) => void;
|
|
38
|
+
activeTab?: number;
|
|
39
|
+
transparent?: boolean;
|
|
40
|
+
drawBehind?: boolean;
|
|
41
|
+
underline?: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
|
|
44
|
+
function getButtonComponents(buttons: TabBarButtonType[], activeTab: number, onSelect: ListenerWithData<number>) {
|
|
45
|
+
return buttons.map((button, index) => {
|
|
46
|
+
const isActive = index === activeTab;
|
|
47
|
+
|
|
48
|
+
const key = button.key ?? String(index);
|
|
49
|
+
if ('component' in button) {
|
|
50
|
+
const Component = button.component;
|
|
51
|
+
return <Component key={key} active={isActive} />;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<TabBarButton key={key} active={isActive} onClickData={index} onClick={onSelect}>
|
|
56
|
+
<Inline>
|
|
57
|
+
{button.icon ? <Icon icon={button.icon} className={styles.buttonIcon} /> : null}
|
|
58
|
+
{button.title ? <Text className={styles.buttonTitle}>{button.title}</Text> : null}
|
|
59
|
+
</Inline>
|
|
60
|
+
</TabBarButton>
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function TabBar({
|
|
66
|
+
buttons,
|
|
67
|
+
startActiveTab,
|
|
68
|
+
onTabChange,
|
|
69
|
+
activeTab,
|
|
70
|
+
transparent,
|
|
71
|
+
underline,
|
|
72
|
+
className,
|
|
73
|
+
...rbmProps
|
|
74
|
+
}: TabBarProps) {
|
|
75
|
+
// States
|
|
76
|
+
const [internalActiveTab, setInternalActiveTab] = useState(startActiveTab ?? 0);
|
|
77
|
+
activeTab = activeTab ?? internalActiveTab;
|
|
78
|
+
|
|
79
|
+
// Refs
|
|
80
|
+
|
|
81
|
+
// Callbacks
|
|
82
|
+
const onSelect = useCallback(
|
|
83
|
+
(_: any, index: number) => {
|
|
84
|
+
setInternalActiveTab(index);
|
|
85
|
+
onTabChange(index);
|
|
86
|
+
},
|
|
87
|
+
[onTabChange, setInternalActiveTab]
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// Effects
|
|
91
|
+
|
|
92
|
+
// Other
|
|
93
|
+
|
|
94
|
+
// Render Functions
|
|
95
|
+
const buttonComponents = getButtonComponents(buttons, activeTab, onSelect);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div
|
|
99
|
+
{...rbmProps}
|
|
100
|
+
className={classNames(
|
|
101
|
+
styles.tabBar,
|
|
102
|
+
{
|
|
103
|
+
[styles.transparent]: transparent,
|
|
104
|
+
[styles.underlined]: underline,
|
|
105
|
+
},
|
|
106
|
+
className
|
|
107
|
+
)}
|
|
108
|
+
>
|
|
109
|
+
<Container fluid="xxl" className={styles.buttonContainer}>
|
|
110
|
+
{buttonComponents}
|
|
111
|
+
</Container>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const TabBarMemo = withMemo(TabBar, styles);
|
|
117
|
+
export { TabBarMemo as TabBar };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { RbmComponentProps } from '../RbmComponentProps';
|
|
3
|
+
|
|
4
|
+
import styles from './tabBar.scss';
|
|
5
|
+
import { withMemo } from '../../helper/withMemo';
|
|
6
|
+
import classNames from 'classnames';
|
|
7
|
+
import { Listener, useListenerWithExtractedProps } from '../Hooks/useListener';
|
|
8
|
+
|
|
9
|
+
export type TabBarButtonProps = RbmComponentProps<
|
|
10
|
+
{
|
|
11
|
+
active: boolean;
|
|
12
|
+
} & Listener<'onClick', number>
|
|
13
|
+
>;
|
|
14
|
+
|
|
15
|
+
function TabBarButton({ active, className, children, ...rbmProps }: TabBarButtonProps) {
|
|
16
|
+
// Variables
|
|
17
|
+
|
|
18
|
+
// States
|
|
19
|
+
|
|
20
|
+
// Refs
|
|
21
|
+
|
|
22
|
+
// Callbacks
|
|
23
|
+
const [onClick, otherProps] = useListenerWithExtractedProps<'onClick', number>('onClick', rbmProps);
|
|
24
|
+
|
|
25
|
+
// Effects
|
|
26
|
+
|
|
27
|
+
// Other
|
|
28
|
+
|
|
29
|
+
// Render Functions
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<a
|
|
33
|
+
{...otherProps}
|
|
34
|
+
role="button"
|
|
35
|
+
onClick={onClick}
|
|
36
|
+
className={classNames(styles.button, { [styles.buttonActive]: active, className })}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</a>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const TabBarButtonMemo = withMemo(TabBarButton, styles);
|
|
44
|
+
export { TabBarButtonMemo as TabBarButton };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
@import "../../scss/variables";
|
|
2
|
+
@import "../../scss/designMixin";
|
|
3
|
+
|
|
4
|
+
.tabBar {
|
|
5
|
+
height: 60px;
|
|
6
|
+
width: 100%;
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-wrap: wrap;
|
|
9
|
+
padding-left: 0;
|
|
10
|
+
margin-bottom: 0;
|
|
11
|
+
list-style: none;
|
|
12
|
+
|
|
13
|
+
.buttonContainer {
|
|
14
|
+
padding: 0;
|
|
15
|
+
display: flex;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&.underlined {
|
|
19
|
+
.buttonActive {
|
|
20
|
+
@include design($flat) {
|
|
21
|
+
border-bottom: 4px solid var(--flavor-accent);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&.transparent {
|
|
27
|
+
opacity: 0.65;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@include design($material) {
|
|
31
|
+
background-color: #ffffff;
|
|
32
|
+
box-shadow: 0 -2px 2px 0px rgb(0 0 0 / 14%), 0 -1px 5px 0px rgb(0 0 0 / 12%), 0 -1px 1px 0px rgb(0 0 0 / 20%);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@include design($flat) {
|
|
36
|
+
background-color: #fafafa;
|
|
37
|
+
border-top: 1px solid var(--border-light);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.button {
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
padding: 0;
|
|
46
|
+
flex-basis: 0;
|
|
47
|
+
flex-grow: 1;
|
|
48
|
+
text-align: center;
|
|
49
|
+
text-decoration: none;
|
|
50
|
+
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
|
|
51
|
+
|
|
52
|
+
> span {
|
|
53
|
+
width: 100%;
|
|
54
|
+
display: inline-block;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.buttonIcon {
|
|
58
|
+
display: block;
|
|
59
|
+
font-size: 24px;
|
|
60
|
+
line-height: 26px;
|
|
61
|
+
height: 28px;
|
|
62
|
+
margin: auto;
|
|
63
|
+
|
|
64
|
+
+ .buttonTitle {
|
|
65
|
+
font-size: 10px;
|
|
66
|
+
line-height: 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.buttonTitle {
|
|
71
|
+
display: block;
|
|
72
|
+
margin: auto;
|
|
73
|
+
font-size: 16px;
|
|
74
|
+
line-height: 49px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@include design($flat) {
|
|
78
|
+
color: #999;
|
|
79
|
+
&.buttonActive {
|
|
80
|
+
color: var(--flavor-accent);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@include design($material) {
|
|
86
|
+
color: #31313a;
|
|
87
|
+
position: relative;
|
|
88
|
+
text-transform: uppercase;
|
|
89
|
+
|
|
90
|
+
&::after {
|
|
91
|
+
content: ' ';
|
|
92
|
+
display: block;
|
|
93
|
+
width: 0;
|
|
94
|
+
height: 2px;
|
|
95
|
+
bottom: 0;
|
|
96
|
+
position: absolute;
|
|
97
|
+
margin-top: -2px;
|
|
98
|
+
background-color: #31313a;
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
&.buttonActive {
|
|
102
|
+
&::after {
|
|
103
|
+
width: 100%;
|
|
104
|
+
transition: width 0.2s ease-in-out;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|