@companix/uikit 0.0.1
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 +54 -0
- package/declaration.d.ts +4 -0
- package/index.html +12 -0
- package/package.json +66 -0
- package/playground/App.tsx +166 -0
- package/playground/Example.tsx +14 -0
- package/playground/Test.tsx +44 -0
- package/playground/animation-test-1/index.scss +20 -0
- package/playground/animation-test-1/index.tsx +17 -0
- package/playground/animation-test-2/index.scss +62 -0
- package/playground/animation-test-2/index.tsx +32 -0
- package/playground/bootstrap.tsx +19 -0
- package/playground/buttons/index.tsx +132 -0
- package/playground/checkbox/index.tsx +64 -0
- package/playground/date-input/index.tsx +45 -0
- package/playground/date-picker/index.tsx +41 -0
- package/playground/dialog/index.tsx +92 -0
- package/playground/dialog-alert/index.tsx +47 -0
- package/playground/drawer/index.tsx +55 -0
- package/playground/index.css +33 -0
- package/playground/index.scss +270 -0
- package/playground/input/index.tsx +112 -0
- package/playground/number-inputs/index.tsx +50 -0
- package/playground/popovers/index.tsx +70 -0
- package/playground/radio-group/index.tsx +69 -0
- package/playground/select/index.tsx +72 -0
- package/playground/select-tags/index.tsx +36 -0
- package/playground/styles.scss +2 -0
- package/playground/switch/index.tsx +44 -0
- package/playground/tabs/index.tsx +16 -0
- package/playground/test.scss +0 -0
- package/playground/text-area/index.tsx +17 -0
- package/playground/text-input/index.tsx +12 -0
- package/playground/toaster/index.tsx +156 -0
- package/playground/tooltip/index.tsx +26 -0
- package/src/Button/Button.scss +128 -0
- package/src/Button/index.tsx +72 -0
- package/src/ButtonGroup/ButtonGroup.scss +18 -0
- package/src/ButtonGroup/index.tsx +20 -0
- package/src/Checkbox/Checkbox.scss +115 -0
- package/src/Checkbox/index.tsx +46 -0
- package/src/Countdown/index.tsx +54 -0
- package/src/DateInput/DateInput.scss +11 -0
- package/src/DateInput/index.tsx +96 -0
- package/src/DatePicker/Calendar.scss +125 -0
- package/src/DatePicker/Calendar.tsx +157 -0
- package/src/DatePicker/CalendarHeader.tsx +139 -0
- package/src/DatePicker/DatePicker.scss +0 -0
- package/src/DatePicker/index.tsx +177 -0
- package/src/Dialog/Dialog.scss +25 -0
- package/src/Dialog/Popup.scss +55 -0
- package/src/Dialog/index.tsx +31 -0
- package/src/DialogAlert/Alert.scss +52 -0
- package/src/DialogAlert/Alert.tsx +78 -0
- package/src/DialogAlert/Viewport.tsx +52 -0
- package/src/DialogAlert/index.tsx +37 -0
- package/src/Drawer/Drawer.scss +112 -0
- package/src/Drawer/index.tsx +46 -0
- package/src/File/index.tsx +60 -0
- package/src/Form/Form.scss +70 -0
- package/src/Form/Input.scss +24 -0
- package/src/Form/index.tsx +131 -0
- package/src/Icon/icon.scss +18 -0
- package/src/Icon/index.tsx +43 -0
- package/src/LoadButton/index.tsx +17 -0
- package/src/NumberInput/index.tsx +74 -0
- package/src/OptionItem/Option.scss +89 -0
- package/src/OptionItem/OptionItem.tsx +49 -0
- package/src/OptionItem/OptionsList.tsx +26 -0
- package/src/Popover/Popover.scss +80 -0
- package/src/Popover/index.tsx +117 -0
- package/src/Radio/Radio.scss +148 -0
- package/src/Radio/index.tsx +68 -0
- package/src/Scrollable/ImitateScroll.tsx +141 -0
- package/src/Scrollable/Scrollable.scss +50 -0
- package/src/Scrollable/index.tsx +141 -0
- package/src/Select/Select.scss +80 -0
- package/src/Select/SelectInput.tsx +131 -0
- package/src/Select/index.tsx +134 -0
- package/src/SelectTags/SelectTags.scss +66 -0
- package/src/SelectTags/index.tsx +192 -0
- package/src/Spinner/Spinner.scss +14 -0
- package/src/Spinner/index.tsx +19 -0
- package/src/Stepper/StepperInput.scss +35 -0
- package/src/Stepper/index.tsx +76 -0
- package/src/Switch/Switch.scss +102 -0
- package/src/Switch/index.tsx +49 -0
- package/src/Tabs/Tabs.scss +58 -0
- package/src/Tabs/index.tsx +89 -0
- package/src/TextArea/TextArea.scss +34 -0
- package/src/TextArea/index.tsx +51 -0
- package/src/Toaster/RemoveListener.tsx +11 -0
- package/src/Toaster/Toast.tsx +69 -0
- package/src/Toaster/Toaster.scss +151 -0
- package/src/Toaster/Viewport.tsx +117 -0
- package/src/Toaster/index.tsx +52 -0
- package/src/Tooltip/Tooltip.scss +28 -0
- package/src/Tooltip/index.tsx +33 -0
- package/src/__hooks/use-frooze-closing.ts +51 -0
- package/src/__hooks/use-loading.ts +34 -0
- package/src/__hooks/use-local-storage.ts +19 -0
- package/src/__hooks/use-popover-position.ts +24 -0
- package/src/__hooks/use-previos.ts +25 -0
- package/src/__hooks/use-resize.ts +41 -0
- package/src/__hooks/use-scrollbox.ts +45 -0
- package/src/__hooks/use-stepper-input.ts +82 -0
- package/src/__hooks/use-update.ts +19 -0
- package/src/__hooks/useCalendar.ts +104 -0
- package/src/__hooks/useCalendarOptions-copy.ts +87 -0
- package/src/__hooks/useCalendarOptions.ts +68 -0
- package/src/__libs/calendar.ts +175 -0
- package/src/__utils/utils.ts +137 -0
- package/src/css.scss +120 -0
- package/src/index.scss +22 -0
- package/src/index.ts +36 -0
- package/src/mixins.scss +99 -0
- package/src/theme.scss +103 -0
- package/src/types.ts +14 -0
- package/tailwind.config.js +91 -0
- package/themes/classic/animations.scss +179 -0
- package/themes/classic/classic.scss +493 -0
- package/tsconfig.json +27 -0
- package/vite.build.ts +35 -0
- package/vite.config.ts +33 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
@use '../mixins.scss';
|
|
2
|
+
|
|
3
|
+
.alert {
|
|
4
|
+
max-height: max-content;
|
|
5
|
+
|
|
6
|
+
@include mixins.use-styles(alert);
|
|
7
|
+
|
|
8
|
+
&-container {
|
|
9
|
+
@include mixins.use-styles(alert, container);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
&-overlay {
|
|
13
|
+
@include mixins.use-styles(alert, overlay);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&-body {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: row;
|
|
19
|
+
|
|
20
|
+
@include mixins.use-styles(alert, body);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
&-title {
|
|
24
|
+
@include mixins.use-styles(alert, title);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&-description {
|
|
28
|
+
@include mixins.use-styles(alert, description);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
&-icon {
|
|
32
|
+
@include mixins.use-styles(alert, icon);
|
|
33
|
+
|
|
34
|
+
svg {
|
|
35
|
+
@include mixins.use-size(alert, icon, size);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&-content {
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
|
|
43
|
+
@include mixins.use-styles(alert, content);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&-footer {
|
|
47
|
+
display: flex;
|
|
48
|
+
justify-content: end;
|
|
49
|
+
|
|
50
|
+
@include mixins.use-styles(alert, footer);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as AlertPrimitive from '@radix-ui/react-alert-dialog'
|
|
2
|
+
import { Button } from '..'
|
|
3
|
+
import { InternButtonProps } from '../Button'
|
|
4
|
+
import { RemoveListener } from '../Toaster/RemoveListener'
|
|
5
|
+
import { VisuallyHidden } from '@radix-ui/react-visually-hidden'
|
|
6
|
+
|
|
7
|
+
interface ActionProps extends Omit<InternButtonProps, 'children'> {
|
|
8
|
+
onClick?: () => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AlertDialogProps {
|
|
12
|
+
open?: boolean
|
|
13
|
+
defaultOpen?: boolean
|
|
14
|
+
onOpenChange?: (value: boolean) => void
|
|
15
|
+
onUnMounted?: () => void
|
|
16
|
+
icon?: React.ReactNode
|
|
17
|
+
title?: string
|
|
18
|
+
description?: string
|
|
19
|
+
confirm?: ActionProps
|
|
20
|
+
cancel?: ActionProps
|
|
21
|
+
cancelDefaultText?: string
|
|
22
|
+
disableCancel?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const AlertDialog = ({
|
|
26
|
+
open,
|
|
27
|
+
defaultOpen,
|
|
28
|
+
onOpenChange,
|
|
29
|
+
onUnMounted,
|
|
30
|
+
icon,
|
|
31
|
+
title,
|
|
32
|
+
description,
|
|
33
|
+
cancelDefaultText,
|
|
34
|
+
cancel,
|
|
35
|
+
disableCancel,
|
|
36
|
+
confirm
|
|
37
|
+
}: AlertDialogProps) => {
|
|
38
|
+
return (
|
|
39
|
+
<AlertPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>
|
|
40
|
+
<RemoveListener callback={onUnMounted} />
|
|
41
|
+
<AlertPrimitive.Portal>
|
|
42
|
+
<AlertPrimitive.Overlay className="popup-overlay dialog-overlay" />
|
|
43
|
+
<AlertPrimitive.Content className="popup-container dialog-container">
|
|
44
|
+
<div className="popup alert">
|
|
45
|
+
<div className="alert-body">
|
|
46
|
+
{icon && <span className="alert-icon">{icon}</span>}
|
|
47
|
+
<div className="alert-content">
|
|
48
|
+
{title && <AlertPrimitive.Title className="alert-title">{title}</AlertPrimitive.Title>}
|
|
49
|
+
{!title && (
|
|
50
|
+
<VisuallyHidden>
|
|
51
|
+
<AlertPrimitive.Title />
|
|
52
|
+
</VisuallyHidden>
|
|
53
|
+
)}
|
|
54
|
+
{description && (
|
|
55
|
+
<AlertPrimitive.Description className="alert-description">
|
|
56
|
+
{description}
|
|
57
|
+
</AlertPrimitive.Description>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div className="alert-footer">
|
|
62
|
+
{!disableCancel && (cancel?.text || cancelDefaultText) && (
|
|
63
|
+
<AlertPrimitive.Cancel asChild>
|
|
64
|
+
<Button appearance="neutral" {...cancel} text={cancel?.text ?? cancelDefaultText} />
|
|
65
|
+
</AlertPrimitive.Cancel>
|
|
66
|
+
)}
|
|
67
|
+
{confirm?.text && (
|
|
68
|
+
<AlertPrimitive.Action asChild>
|
|
69
|
+
<Button appearance="negative" {...confirm} />
|
|
70
|
+
</AlertPrimitive.Action>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</AlertPrimitive.Content>
|
|
75
|
+
</AlertPrimitive.Portal>
|
|
76
|
+
</AlertPrimitive.Root>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { forwardRef, useImperativeHandle, useState } from 'react'
|
|
2
|
+
import { InnerAlert } from '.'
|
|
3
|
+
import { AlertDialog, AlertDialogProps } from './Alert'
|
|
4
|
+
|
|
5
|
+
export interface ViewportRef {
|
|
6
|
+
showAlert: (alert: InnerAlert) => void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface AlertBaseProps extends Pick<AlertDialogProps, 'cancelDefaultText'> {}
|
|
10
|
+
|
|
11
|
+
export const Viewport = forwardRef<ViewportRef, AlertBaseProps>((props, ref) => {
|
|
12
|
+
const [alerts, setAlerts] = useState<InnerAlert[]>([])
|
|
13
|
+
|
|
14
|
+
useImperativeHandle(
|
|
15
|
+
ref,
|
|
16
|
+
() => {
|
|
17
|
+
return {
|
|
18
|
+
showAlert: (alert) => {
|
|
19
|
+
setAlerts((state) => [...state, alert])
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
[]
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
const handleClose = (id: string) => {
|
|
27
|
+
setAlerts((state) => {
|
|
28
|
+
const nextState = [...state]
|
|
29
|
+
const index = nextState.findIndex((item) => item.id === id)
|
|
30
|
+
|
|
31
|
+
if (index !== -1) {
|
|
32
|
+
nextState.splice(index, 1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return nextState
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
{alerts.map(({ id, ...alert }) => (
|
|
42
|
+
<AlertDialog
|
|
43
|
+
defaultOpen
|
|
44
|
+
onUnMounted={() => handleClose(id)}
|
|
45
|
+
key={`alert-${id}`}
|
|
46
|
+
{...props}
|
|
47
|
+
{...alert}
|
|
48
|
+
/>
|
|
49
|
+
))}
|
|
50
|
+
</>
|
|
51
|
+
)
|
|
52
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { hash } from '@companix/utils-js'
|
|
2
|
+
import { useMemo, useRef } from 'react'
|
|
3
|
+
import { AlertDialogProps } from './Alert'
|
|
4
|
+
import { AlertBaseProps, Viewport, ViewportRef } from './Viewport'
|
|
5
|
+
|
|
6
|
+
export interface AlertOptions extends Omit<AlertDialogProps, 'open' | 'onOpenChange'> {}
|
|
7
|
+
|
|
8
|
+
export interface InnerAlert extends AlertOptions {
|
|
9
|
+
id: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const createAlertAgent = (options: AlertBaseProps = {}) => {
|
|
13
|
+
const store = {
|
|
14
|
+
emit: (alert: InnerAlert) => {
|
|
15
|
+
console.error('uninitialized', alert)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
show: (value: Omit<InnerAlert, 'id'>) => {
|
|
21
|
+
store.emit({ ...value, id: hash() })
|
|
22
|
+
},
|
|
23
|
+
Viewport: () => {
|
|
24
|
+
const ref = useRef<ViewportRef>(null)
|
|
25
|
+
|
|
26
|
+
useMemo(() => {
|
|
27
|
+
store.emit = (value) => {
|
|
28
|
+
if (ref.current) {
|
|
29
|
+
ref.current.showAlert(value)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}, [])
|
|
33
|
+
|
|
34
|
+
return <Viewport ref={ref} {...options} />
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
@use '../mixins.scss';
|
|
2
|
+
|
|
3
|
+
.drawer {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
max-height: 100%;
|
|
7
|
+
z-index: 1000;
|
|
8
|
+
outline: none;
|
|
9
|
+
pointer-events: auto;
|
|
10
|
+
position: fixed;
|
|
11
|
+
z-index: 9999;
|
|
12
|
+
|
|
13
|
+
@include mixins.use-styles(drawer);
|
|
14
|
+
|
|
15
|
+
&-overlay {
|
|
16
|
+
inset: 0;
|
|
17
|
+
overflow: auto;
|
|
18
|
+
position: fixed;
|
|
19
|
+
-webkit-user-select: none;
|
|
20
|
+
-moz-user-select: none;
|
|
21
|
+
-ms-user-select: none;
|
|
22
|
+
user-select: none;
|
|
23
|
+
outline: none;
|
|
24
|
+
z-index: 9999;
|
|
25
|
+
|
|
26
|
+
@include mixins.use-styles(drawer, overlay);
|
|
27
|
+
|
|
28
|
+
&[data-state='open'] {
|
|
29
|
+
@include mixins.use-styles(drawer, overlay, in);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&[data-state='closed'] {
|
|
33
|
+
@include mixins.use-styles(drawer, overlay, out);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// animations
|
|
38
|
+
|
|
39
|
+
&[data-state='open'] {
|
|
40
|
+
&[data-direction='left'] {
|
|
41
|
+
@include mixins.use-styles(drawer, in, left);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&[data-direction='right'] {
|
|
45
|
+
@include mixins.use-styles(drawer, in, right);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&[data-direction='top'] {
|
|
49
|
+
@include mixins.use-styles(drawer, in, top);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&[data-direction='bottom'] {
|
|
53
|
+
@include mixins.use-styles(drawer, in, bottom);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&[data-state='closed'] {
|
|
58
|
+
&[data-direction='left'] {
|
|
59
|
+
@include mixins.use-styles(drawer, out, left);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&[data-direction='right'] {
|
|
63
|
+
@include mixins.use-styles(drawer, out, right);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&[data-direction='top'] {
|
|
67
|
+
@include mixins.use-styles(drawer, out, top);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&[data-direction='bottom'] {
|
|
71
|
+
@include mixins.use-styles(drawer, out, bottom);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// placement
|
|
76
|
+
|
|
77
|
+
&[data-direction='left'] {
|
|
78
|
+
top: 0;
|
|
79
|
+
left: 0;
|
|
80
|
+
bottom: 0;
|
|
81
|
+
width: var(--drawer-size, 50%);
|
|
82
|
+
border-top-right-radius: var(--drawer_radius, 0);
|
|
83
|
+
border-bottom-right-radius: var(--drawer_radius, 0);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
&[data-direction='right'] {
|
|
87
|
+
top: 0;
|
|
88
|
+
right: 0;
|
|
89
|
+
bottom: 0;
|
|
90
|
+
width: var(--drawer-size, 50%);
|
|
91
|
+
border-top-left-radius: var(--drawer_radius, 0);
|
|
92
|
+
border-bottom-left-radius: var(--drawer_radius, 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&[data-direction='bottom'] {
|
|
96
|
+
left: 0;
|
|
97
|
+
right: 0;
|
|
98
|
+
bottom: 0;
|
|
99
|
+
height: var(--drawer-size, 50%);
|
|
100
|
+
border-top-right-radius: var(--drawer_radius, 0);
|
|
101
|
+
border-top-left-radius: var(--drawer_radius, 0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
&[data-direction='top'] {
|
|
105
|
+
left: 0;
|
|
106
|
+
right: 0;
|
|
107
|
+
top: 0;
|
|
108
|
+
height: var(--drawer-size, 50%);
|
|
109
|
+
border-bottom-right-radius: var(--drawer_radius, 0);
|
|
110
|
+
border-bottom-left-radius: var(--drawer_radius, 0);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
|
3
|
+
import { VisuallyHidden } from '@radix-ui/react-visually-hidden'
|
|
4
|
+
import { varToStyle } from '@companix/utils-browser'
|
|
5
|
+
|
|
6
|
+
export interface DrawerProps {
|
|
7
|
+
open: boolean
|
|
8
|
+
onOpenChange: (value: boolean) => void
|
|
9
|
+
children: React.ReactNode
|
|
10
|
+
direction?: 'bottom' | 'top' | 'left' | 'right'
|
|
11
|
+
className?: string
|
|
12
|
+
/**
|
|
13
|
+
* CSS size of the drawer. This sets `width` if horizontal position (default)
|
|
14
|
+
* and `height` otherwise.
|
|
15
|
+
*
|
|
16
|
+
* Constants are available for common sizes:
|
|
17
|
+
* - `DrawerSize.SMALL = 360px`
|
|
18
|
+
* - `DrawerSize.STANDARD = 50%`
|
|
19
|
+
* - `DrawerSize.LARGE = 90%`
|
|
20
|
+
*
|
|
21
|
+
* @default DrawerSize.STANDARD = "50%"
|
|
22
|
+
*/
|
|
23
|
+
size?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const Drawer = ({ open, onOpenChange, children, size, direction, className }: DrawerProps) => {
|
|
27
|
+
return (
|
|
28
|
+
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
|
29
|
+
<DialogPrimitive.Portal>
|
|
30
|
+
<DialogPrimitive.Overlay className="drawer-overlay" />
|
|
31
|
+
<DialogPrimitive.Content
|
|
32
|
+
style={varToStyle({ '--drawer-size': size ?? '50%' })}
|
|
33
|
+
className={classNames('drawer', className)}
|
|
34
|
+
data-direction={direction}
|
|
35
|
+
>
|
|
36
|
+
<VisuallyHidden>
|
|
37
|
+
<DialogPrimitive.Title />
|
|
38
|
+
</VisuallyHidden>
|
|
39
|
+
{children}
|
|
40
|
+
</DialogPrimitive.Content>
|
|
41
|
+
</DialogPrimitive.Portal>
|
|
42
|
+
</DialogPrimitive.Root>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Drawer.Close = DialogPrimitive.Close
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { VisuallyHidden } from '@radix-ui/react-visually-hidden'
|
|
2
|
+
import { useRef } from 'react'
|
|
3
|
+
|
|
4
|
+
export interface FileOverlayProps {
|
|
5
|
+
multiple?: true
|
|
6
|
+
onChange?: (file: File[]) => void
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
mimes?: string[]
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
className?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const FileOverlay = ({
|
|
14
|
+
onChange,
|
|
15
|
+
disabled,
|
|
16
|
+
mimes,
|
|
17
|
+
children,
|
|
18
|
+
multiple,
|
|
19
|
+
className
|
|
20
|
+
}: FileOverlayProps) => {
|
|
21
|
+
const ref = useRef<HTMLInputElement>(null)
|
|
22
|
+
|
|
23
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
24
|
+
if (disabled) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (e.target.files) {
|
|
29
|
+
onChange?.(Array.from(e.target.files))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (ref.current) {
|
|
33
|
+
ref.current.value = ''
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<label className={className}>
|
|
39
|
+
{children}
|
|
40
|
+
<VisuallyHidden asChild>
|
|
41
|
+
<input
|
|
42
|
+
ref={ref}
|
|
43
|
+
hidden
|
|
44
|
+
type="file"
|
|
45
|
+
autoComplete="off"
|
|
46
|
+
autoCapitalize="none"
|
|
47
|
+
autoCorrect="off"
|
|
48
|
+
spellCheck="false"
|
|
49
|
+
aria-autocomplete="none"
|
|
50
|
+
multiple={multiple}
|
|
51
|
+
accept={(mimes ?? []).join(', ')}
|
|
52
|
+
onChange={handleChange}
|
|
53
|
+
disabled={disabled}
|
|
54
|
+
/>
|
|
55
|
+
</VisuallyHidden>
|
|
56
|
+
</label>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { FileOverlay }
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
@use '../mixins.scss';
|
|
2
|
+
|
|
3
|
+
@mixin use-size($size) {
|
|
4
|
+
&[data-size='#{$size}'] {
|
|
5
|
+
.form-input {
|
|
6
|
+
@include mixins.use-styles(form, size, $size);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.form {
|
|
12
|
+
position: relative;
|
|
13
|
+
|
|
14
|
+
@include mixins.use-styles(form);
|
|
15
|
+
|
|
16
|
+
@include use-size(sm);
|
|
17
|
+
@include use-size(md);
|
|
18
|
+
@include use-size(lg);
|
|
19
|
+
|
|
20
|
+
&-space-margin {
|
|
21
|
+
margin: 0px var(--form_space, 0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&:hover {
|
|
25
|
+
@include mixins.use-styles(form, hover);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:active {
|
|
29
|
+
@include mixins.use-styles(form, active);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&[data-fill] {
|
|
33
|
+
width: 100%;
|
|
34
|
+
|
|
35
|
+
.form-input {
|
|
36
|
+
width: 100%;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&[data-required] {
|
|
41
|
+
@include mixins.use-styles(form, required);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&[data-disabled] {
|
|
45
|
+
@include mixins.use-styles(form, disabled);
|
|
46
|
+
|
|
47
|
+
.form-input {
|
|
48
|
+
cursor: inherit;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&:focus-within {
|
|
53
|
+
@include mixins.use-styles(form, focus);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.form-input {
|
|
58
|
+
outline: none;
|
|
59
|
+
|
|
60
|
+
&::-webkit-outer-spin-button,
|
|
61
|
+
&::-webkit-inner-spin-button {
|
|
62
|
+
-webkit-appearance: none;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&::placeholder {
|
|
66
|
+
user-select: none;
|
|
67
|
+
|
|
68
|
+
@include mixins.use-styles(form, placeholder);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.form-input-base {
|
|
2
|
+
padding: 0px var(--form_space, 0);
|
|
3
|
+
border-radius: var(--form_border-radius);
|
|
4
|
+
|
|
5
|
+
&-left-element {
|
|
6
|
+
position: absolute;
|
|
7
|
+
left: 0;
|
|
8
|
+
top: 0;
|
|
9
|
+
bottom: 0;
|
|
10
|
+
pointer-events: none;
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&-right-element {
|
|
16
|
+
position: absolute;
|
|
17
|
+
right: 0;
|
|
18
|
+
top: 0;
|
|
19
|
+
bottom: 0;
|
|
20
|
+
pointer-events: none;
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import cn from 'classnames'
|
|
2
|
+
import InputMask from 'react-input-mask'
|
|
3
|
+
|
|
4
|
+
import { useLayoutAndUpdate } from '../__hooks/use-update'
|
|
5
|
+
import { attr } from '@companix/utils-browser'
|
|
6
|
+
import { forwardRef, useCallback, useRef } from 'react'
|
|
7
|
+
import { mergeRefs } from 'react-merge-refs'
|
|
8
|
+
|
|
9
|
+
export interface FormProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
10
|
+
required?: boolean
|
|
11
|
+
disabled?: boolean
|
|
12
|
+
leftElement?: React.ReactNode
|
|
13
|
+
rightElement?: React.ReactNode
|
|
14
|
+
placeholder?: string
|
|
15
|
+
value?: string | number
|
|
16
|
+
readOnly?: boolean
|
|
17
|
+
onValueChange?: (value: string, targetElement: HTMLInputElement) => void
|
|
18
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
19
|
+
inputRef?: React.RefObject<HTMLInputElement>
|
|
20
|
+
size?: 'sm' | 'md' | 'lg'
|
|
21
|
+
fill?: boolean
|
|
22
|
+
mask?: string
|
|
23
|
+
maskChar?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const Form = forwardRef<HTMLDivElement, FormProps>(
|
|
27
|
+
(
|
|
28
|
+
{
|
|
29
|
+
required,
|
|
30
|
+
size,
|
|
31
|
+
fill,
|
|
32
|
+
leftElement,
|
|
33
|
+
rightElement,
|
|
34
|
+
onChange,
|
|
35
|
+
onValueChange,
|
|
36
|
+
readOnly,
|
|
37
|
+
className,
|
|
38
|
+
value,
|
|
39
|
+
placeholder,
|
|
40
|
+
disabled,
|
|
41
|
+
mask,
|
|
42
|
+
maskChar,
|
|
43
|
+
inputRef: clientInputRef,
|
|
44
|
+
...containerProps
|
|
45
|
+
},
|
|
46
|
+
ref
|
|
47
|
+
) => {
|
|
48
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
49
|
+
|
|
50
|
+
const rightRef = useRef<HTMLSpanElement>(null)
|
|
51
|
+
const leftRef = useRef<HTMLSpanElement>(null)
|
|
52
|
+
|
|
53
|
+
const elements = { Right: rightRef, Left: leftRef }
|
|
54
|
+
|
|
55
|
+
const updateInputWidth = useCallback((side: 'Left' | 'Right') => {
|
|
56
|
+
if (inputRef.current) {
|
|
57
|
+
const input = inputRef.current.style
|
|
58
|
+
const element = elements[side]
|
|
59
|
+
|
|
60
|
+
if (element.current && element.current.clientWidth) {
|
|
61
|
+
if (input[`padding${side}`] !== `${element.current.clientWidth}px`) {
|
|
62
|
+
input[`padding${side}`] = `${element.current.clientWidth}px`
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
if (input[`padding${side}`]) {
|
|
66
|
+
input[`padding${side}`] = ''
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}, [])
|
|
71
|
+
|
|
72
|
+
useLayoutAndUpdate(() => {
|
|
73
|
+
updateInputWidth('Left')
|
|
74
|
+
updateInputWidth('Right')
|
|
75
|
+
}, [rightElement, leftElement])
|
|
76
|
+
|
|
77
|
+
const handleInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
|
78
|
+
onChange?.(event)
|
|
79
|
+
onValueChange?.(event.target.value, event.target)
|
|
80
|
+
}, [])
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
ref={ref}
|
|
85
|
+
className={cn('form', className)}
|
|
86
|
+
data-size={size ?? 'md'}
|
|
87
|
+
data-fill={attr(fill)}
|
|
88
|
+
data-required={attr(required)}
|
|
89
|
+
data-disabled={attr(disabled)}
|
|
90
|
+
{...containerProps}
|
|
91
|
+
>
|
|
92
|
+
{leftElement && (
|
|
93
|
+
<span ref={leftRef} className="form-input-base-left-element">
|
|
94
|
+
{leftElement}
|
|
95
|
+
</span>
|
|
96
|
+
)}
|
|
97
|
+
<Input
|
|
98
|
+
type="text"
|
|
99
|
+
ref={mergeRefs([inputRef, clientInputRef])}
|
|
100
|
+
className="form-input form-input-base"
|
|
101
|
+
aria-disabled={disabled}
|
|
102
|
+
onChange={handleInputChange}
|
|
103
|
+
value={value}
|
|
104
|
+
placeholder={placeholder}
|
|
105
|
+
disabled={disabled}
|
|
106
|
+
readOnly={readOnly}
|
|
107
|
+
maskChar={maskChar}
|
|
108
|
+
mask={mask}
|
|
109
|
+
/>
|
|
110
|
+
{rightElement && (
|
|
111
|
+
<span ref={rightRef} className="form-input-base-right-element">
|
|
112
|
+
{rightElement}
|
|
113
|
+
</span>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'children'> {
|
|
121
|
+
mask?: string
|
|
122
|
+
maskChar?: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const Input = forwardRef<HTMLInputElement, InputProps>(({ mask, maskChar, ...inputProps }, ref) => {
|
|
126
|
+
if (mask) {
|
|
127
|
+
return <InputMask inputRef={ref} mask={mask} maskChar={maskChar} {...inputProps} />
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return <input ref={ref} {...inputProps} />
|
|
131
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@use '../mixins.scss';
|
|
2
|
+
|
|
3
|
+
$icon-sizes: xxxs, xxs, xs, s, m, l, xl, xxl, xxxl;
|
|
4
|
+
|
|
5
|
+
@each $size in $icon-sizes {
|
|
6
|
+
.icon-size-#{$size} {
|
|
7
|
+
@include mixins.use-size(icon, size, $size);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
:where(svg:not([fill])) {
|
|
12
|
+
fill: currentcolor;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.icon {
|
|
16
|
+
// display: inline-block;
|
|
17
|
+
flex-shrink: 0;
|
|
18
|
+
}
|