@geckou/ui-react 0.1.0 → 0.2.0
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/README.md +46 -5
- package/dist/components/BasicButton.d.ts +14 -0
- package/dist/components/BasicButton.js +57 -0
- package/dist/components/CheckBox.d.ts +13 -0
- package/dist/components/CheckBox.js +74 -0
- package/dist/components/CheckBoxes.d.ts +12 -0
- package/dist/components/CheckBoxes.js +68 -0
- package/dist/components/CheckButton.d.ts +13 -0
- package/dist/components/CheckButton.js +56 -0
- package/dist/components/DatePicker.d.ts +18 -0
- package/dist/components/DatePicker.js +131 -0
- package/dist/components/DateRangePicker.d.ts +22 -0
- package/dist/components/DateRangePicker.js +65 -0
- package/dist/components/DateSelector.d.ts +13 -0
- package/dist/components/DateSelector.js +129 -0
- package/dist/components/DropdownUi.d.ts +16 -0
- package/dist/components/DropdownUi.js +88 -0
- package/dist/components/ErrorMessage.d.ts +9 -0
- package/dist/components/ErrorMessage.js +21 -0
- package/dist/components/FileInput.d.ts +7 -0
- package/dist/components/FileInput.js +91 -0
- package/dist/components/InputBox.d.ts +11 -0
- package/dist/components/InputBox.js +75 -0
- package/dist/components/InputGroup.d.ts +6 -0
- package/dist/components/InputGroup.js +7 -0
- package/dist/components/LabeledCheckbox.d.ts +12 -0
- package/dist/components/LabeledCheckbox.js +46 -0
- package/dist/components/LabeledFieldset.d.ts +8 -0
- package/dist/components/LabeledFieldset.js +10 -0
- package/dist/components/LoadingSpinner.d.ts +1 -0
- package/dist/components/LoadingSpinner.js +26 -0
- package/dist/components/ModalBox.d.ts +12 -0
- package/dist/components/ModalBox.js +83 -0
- package/dist/components/PopupBox.d.ts +14 -0
- package/dist/components/PopupBox.js +41 -0
- package/dist/components/RadioButtons.d.ts +13 -0
- package/dist/components/RadioButtons.js +64 -0
- package/dist/components/SearchableSelectBox.d.ts +14 -0
- package/dist/components/SearchableSelectBox.js +61 -0
- package/dist/components/SelectBox.d.ts +16 -0
- package/dist/components/SelectBox.js +107 -0
- package/dist/components/SlideDownUi.d.ts +17 -0
- package/dist/components/SlideDownUi.js +86 -0
- package/dist/components/TabUI.d.ts +19 -0
- package/dist/components/TabUI.js +76 -0
- package/dist/components/TextArea.d.ts +18 -0
- package/dist/components/TextArea.js +75 -0
- package/dist/components/TextBox.d.ts +20 -0
- package/dist/components/TextBox.js +90 -0
- package/dist/components/ToggleButton.d.ts +15 -0
- package/dist/components/ToggleButton.js +92 -0
- package/dist/components/icons/BackupIcon.d.ts +5 -0
- package/dist/components/icons/BackupIcon.js +16 -0
- package/dist/components/icons/CalendarIcon.d.ts +5 -0
- package/dist/components/icons/CalendarIcon.js +15 -0
- package/dist/components/icons/CheckIcon.d.ts +1 -0
- package/dist/components/icons/CheckIcon.js +7 -0
- package/dist/components/icons/CloseIcon.d.ts +5 -0
- package/dist/components/icons/CloseIcon.js +16 -0
- package/dist/components/icons/KeyboardArrowDownIcon.d.ts +5 -0
- package/dist/components/icons/KeyboardArrowDownIcon.js +16 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/hooks/useFormValidation.d.ts +33 -0
- package/dist/hooks/useFormValidation.js +28 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +70 -0
- package/{src → dist}/styles/tokens.css +1 -1
- package/dist/types/index.d.ts +1 -0
- package/package.json +17 -12
- package/src/components/BasicButton.tsx +0 -81
- package/src/components/CheckBox.tsx +0 -87
- package/src/components/CheckBoxes.tsx +0 -93
- package/src/components/CheckButton.tsx +0 -66
- package/src/components/DatePicker.tsx +0 -154
- package/src/components/DateRangePicker.tsx +0 -59
- package/src/components/DateSelector.tsx +0 -162
- package/src/components/DropdownUi.tsx +0 -126
- package/src/components/ErrorMessage.tsx +0 -30
- package/src/components/FileInput.tsx +0 -93
- package/src/components/InputBox.tsx +0 -118
- package/src/components/InputGroup.tsx +0 -13
- package/src/components/LabeledCheckbox.tsx +0 -61
- package/src/components/LabeledFieldset.tsx +0 -18
- package/src/components/LoadingSpinner.tsx +0 -23
- package/src/components/ModalBox.tsx +0 -109
- package/src/components/PopupBox.tsx +0 -70
- package/src/components/RadioButtons.tsx +0 -94
- package/src/components/SearchableSelectBox.tsx +0 -108
- package/src/components/SelectBox.tsx +0 -152
- package/src/components/SlideDownUi.tsx +0 -123
- package/src/components/TabUI.tsx +0 -112
- package/src/components/TextArea.tsx +0 -102
- package/src/components/TextBox.tsx +0 -120
- package/src/components/ToggleButton.tsx +0 -102
- package/src/components/icons/BackupIcon.tsx +0 -16
- package/src/components/icons/CalendarIcon.tsx +0 -15
- package/src/components/icons/CheckIcon.tsx +0 -7
- package/src/components/icons/CloseIcon.tsx +0 -16
- package/src/components/icons/KeyboardArrowDownIcon.tsx +0 -16
- package/src/constants/index.ts +0 -2
- package/src/hooks/useFormValidation.ts +0 -69
- package/src/index.ts +0 -66
- package/src/types/index.ts +0 -23
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { CSSProperties, MouseEventHandler, ReactNode } from 'react'
|
|
4
|
-
import type { ButtonStyleForEachStatus } from '../types'
|
|
5
|
-
import { LoadingSpinner } from './LoadingSpinner'
|
|
6
|
-
import { COLOR } from '../constants'
|
|
7
|
-
|
|
8
|
-
type Props = {
|
|
9
|
-
cssStyle?: ButtonStyleForEachStatus
|
|
10
|
-
duration?: number
|
|
11
|
-
isLoading?: boolean
|
|
12
|
-
isDisabled?: boolean
|
|
13
|
-
buttonType?: 'button' | 'submit' | 'reset'
|
|
14
|
-
onClick?: MouseEventHandler<HTMLButtonElement>
|
|
15
|
-
loading?: ReactNode
|
|
16
|
-
children?: ReactNode
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function BasicButton({
|
|
20
|
-
cssStyle,
|
|
21
|
-
duration = 0.3,
|
|
22
|
-
isLoading,
|
|
23
|
-
isDisabled,
|
|
24
|
-
buttonType = 'button',
|
|
25
|
-
onClick,
|
|
26
|
-
loading,
|
|
27
|
-
children,
|
|
28
|
-
}: Props) {
|
|
29
|
-
const baseStyle = isDisabled ? cssStyle?.disabled : cssStyle?.default
|
|
30
|
-
|
|
31
|
-
const currentCssStyle = {
|
|
32
|
-
textColor: COLOR.white,
|
|
33
|
-
backgroundColor: isDisabled ? COLOR.darkGray : COLOR.blue,
|
|
34
|
-
backgroundImage: 'none',
|
|
35
|
-
border: {
|
|
36
|
-
color: isDisabled ? COLOR.darkGray : COLOR.blue,
|
|
37
|
-
size: '1px',
|
|
38
|
-
radius: '.25rem',
|
|
39
|
-
},
|
|
40
|
-
boxShadow: '0 0 0 0 transparent',
|
|
41
|
-
...baseStyle,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const hoverStyle = cssStyle?.hover || {}
|
|
45
|
-
|
|
46
|
-
const style = {
|
|
47
|
-
'--text-color': currentCssStyle.textColor,
|
|
48
|
-
'--background-color': currentCssStyle.backgroundColor,
|
|
49
|
-
'--background-image': currentCssStyle.backgroundImage,
|
|
50
|
-
'--radius-size': currentCssStyle.border?.radius,
|
|
51
|
-
'--button-shadow': `0 0 0 ${currentCssStyle.border?.size} ${currentCssStyle.border?.color} inset, ${currentCssStyle.boxShadow}`,
|
|
52
|
-
'--hover-text-color': hoverStyle.textColor || currentCssStyle.textColor,
|
|
53
|
-
'--hover-background-color':
|
|
54
|
-
hoverStyle.backgroundColor ||
|
|
55
|
-
(currentCssStyle.backgroundColor &&
|
|
56
|
-
`${currentCssStyle.backgroundColor}cc`),
|
|
57
|
-
'--hover-background-image':
|
|
58
|
-
hoverStyle.backgroundImage || currentCssStyle.backgroundImage,
|
|
59
|
-
'--hover-radius-size':
|
|
60
|
-
hoverStyle.border?.radius || currentCssStyle.border?.radius,
|
|
61
|
-
'--hover-button-shadow': `0 0 0 ${hoverStyle.border?.size || currentCssStyle.border?.size} ${hoverStyle.border?.color || currentCssStyle.border?.color} inset, ${hoverStyle.boxShadow || currentCssStyle.boxShadow}`,
|
|
62
|
-
'--duration': `${duration}s`,
|
|
63
|
-
} as CSSProperties
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<button
|
|
67
|
-
type={buttonType}
|
|
68
|
-
disabled={isDisabled || isLoading}
|
|
69
|
-
onClick={onClick}
|
|
70
|
-
style={style}
|
|
71
|
-
className="relative inline-flex w-max cursor-pointer items-center justify-center rounded-(--radius-size) border-none bg-(--background-color) px-8 py-4 leading-none text-(--text-color) shadow-(--button-shadow) transition-all duration-(--duration) enabled:hover:rounded-(--hover-radius-size) enabled:hover:bg-(--hover-background-color) enabled:hover:text-(--hover-text-color) enabled:hover:shadow-(--hover-button-shadow) disabled:pointer-events-none disabled:before:pointer-events-auto disabled:before:absolute disabled:before:inset-0 disabled:before:cursor-not-allowed disabled:before:content-['']"
|
|
72
|
-
>
|
|
73
|
-
{isLoading && (
|
|
74
|
-
<span className="absolute inset-0 m-auto flex max-h-[calc(100%-1.5rem)] items-center justify-center fill-current text-current [&_*]:max-h-full [&_*]:fill-current">
|
|
75
|
-
{loading ?? <LoadingSpinner />}
|
|
76
|
-
</span>
|
|
77
|
-
)}
|
|
78
|
-
<span className={isLoading ? 'invisible' : undefined}>{children}</span>
|
|
79
|
-
</button>
|
|
80
|
-
)
|
|
81
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { CSSProperties, ReactNode } from 'react'
|
|
4
|
-
import type { CheckBoxStyleForEachStatus } from '../types'
|
|
5
|
-
import { CheckIcon } from './icons/CheckIcon'
|
|
6
|
-
import { COLOR } from '../constants'
|
|
7
|
-
|
|
8
|
-
type Props = {
|
|
9
|
-
name: string
|
|
10
|
-
checked?: boolean
|
|
11
|
-
onChange?: (newValue: boolean) => void
|
|
12
|
-
isDisabled?: boolean
|
|
13
|
-
cssStyle?: CheckBoxStyleForEachStatus
|
|
14
|
-
isDisableAnimation?: boolean
|
|
15
|
-
check?: ReactNode
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function CheckBox({
|
|
19
|
-
name,
|
|
20
|
-
checked,
|
|
21
|
-
onChange,
|
|
22
|
-
isDisabled,
|
|
23
|
-
cssStyle,
|
|
24
|
-
isDisableAnimation,
|
|
25
|
-
check,
|
|
26
|
-
}: Props) {
|
|
27
|
-
const isChecked = checked ?? false
|
|
28
|
-
const baseStyle = isDisabled ? cssStyle?.disabled : cssStyle?.default
|
|
29
|
-
|
|
30
|
-
const currentCssStyle = {
|
|
31
|
-
textColor: isDisabled ? COLOR.darkGray : COLOR.blue,
|
|
32
|
-
backgroundColor: isDisabled ? COLOR.lightGray : COLOR.white,
|
|
33
|
-
border: {
|
|
34
|
-
color: isDisabled ? COLOR.darkGray : COLOR.blue,
|
|
35
|
-
size: '1px',
|
|
36
|
-
radius: '.25rem',
|
|
37
|
-
},
|
|
38
|
-
...baseStyle,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const style = {
|
|
42
|
-
'--text-color': currentCssStyle.textColor,
|
|
43
|
-
'--border-color': currentCssStyle.border?.color,
|
|
44
|
-
'--border-size': currentCssStyle.border?.size,
|
|
45
|
-
'--radius-size': currentCssStyle.border?.radius,
|
|
46
|
-
'--background-color': currentCssStyle.backgroundColor,
|
|
47
|
-
'--duration': isDisableAnimation ? '0s' : '.3s',
|
|
48
|
-
} as CSSProperties
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<>
|
|
52
|
-
<style>
|
|
53
|
-
{
|
|
54
|
-
'@keyframes uiCheckPop{0%{scale:1}10%{scale:.8}50%{scale:1.1}100%{scale:1}}'
|
|
55
|
-
}
|
|
56
|
-
</style>
|
|
57
|
-
<button
|
|
58
|
-
type="button"
|
|
59
|
-
style={style}
|
|
60
|
-
aria-label={name}
|
|
61
|
-
aria-pressed={isChecked}
|
|
62
|
-
disabled={isDisabled}
|
|
63
|
-
onClick={(event) => {
|
|
64
|
-
event.stopPropagation()
|
|
65
|
-
if (!isDisabled) onChange?.(!isChecked)
|
|
66
|
-
}}
|
|
67
|
-
className={`relative flex h-6 w-6 cursor-pointer items-center justify-center rounded-(--radius-size) border-none shadow-[0_0_0_var(--border-size)_var(--border-color)_inset] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--border-color) has-[input:disabled]:pointer-events-none has-[input:disabled]:before:pointer-events-auto has-[input:disabled]:before:absolute has-[input:disabled]:before:inset-0 has-[input:disabled]:before:cursor-not-allowed has-[input:disabled]:before:content-[''] ${isChecked ? 'animate-[uiCheckPop_var(--duration)_ease-out] bg-(--border-color)' : 'bg-(--background-color)'}`}
|
|
68
|
-
>
|
|
69
|
-
<input
|
|
70
|
-
type="checkbox"
|
|
71
|
-
name={name}
|
|
72
|
-
checked={isChecked}
|
|
73
|
-
disabled={isDisabled}
|
|
74
|
-
onChange={(event) => {
|
|
75
|
-
if (!isDisabled) onChange?.(event.target.checked)
|
|
76
|
-
}}
|
|
77
|
-
className="hidden"
|
|
78
|
-
/>
|
|
79
|
-
<div
|
|
80
|
-
className={`flex h-4 w-4 items-center justify-center [&>*]:fill-current [&>*]:text-current ${isChecked ? 'text-(--background-color)' : 'text-(--border-color)'}`}
|
|
81
|
-
>
|
|
82
|
-
{check ?? <CheckIcon />}
|
|
83
|
-
</div>
|
|
84
|
-
</button>
|
|
85
|
-
</>
|
|
86
|
-
)
|
|
87
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { Option, CheckBoxStyleForEachStatus } from '../types'
|
|
4
|
-
import { MESSAGES } from '@geckou/ui-core'
|
|
5
|
-
import { useEffect, useRef, useState } from 'react'
|
|
6
|
-
import { LabeledCheckbox } from './LabeledCheckbox'
|
|
7
|
-
import { ErrorMessage } from './ErrorMessage'
|
|
8
|
-
import { COLOR } from '../constants'
|
|
9
|
-
|
|
10
|
-
type Props = {
|
|
11
|
-
name: string
|
|
12
|
-
options: Option[]
|
|
13
|
-
value?: string[]
|
|
14
|
-
onChange?: (newValue: string[]) => void
|
|
15
|
-
isDisabled?: boolean
|
|
16
|
-
isRequired?: boolean
|
|
17
|
-
cssStyle?: CheckBoxStyleForEachStatus
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function CheckBoxes({
|
|
21
|
-
name,
|
|
22
|
-
options,
|
|
23
|
-
value,
|
|
24
|
-
onChange,
|
|
25
|
-
isDisabled,
|
|
26
|
-
isRequired,
|
|
27
|
-
cssStyle,
|
|
28
|
-
}: Props) {
|
|
29
|
-
const [checkedValues, setCheckedValues] = useState<string[]>(value ?? [])
|
|
30
|
-
const [errorMessage, setErrorMessage] = useState('')
|
|
31
|
-
|
|
32
|
-
const validateInput = (newValues: string[]) =>
|
|
33
|
-
setErrorMessage(isRequired && newValues.length === 0 ? MESSAGES.required : '')
|
|
34
|
-
|
|
35
|
-
const isFirstRender = useRef(true)
|
|
36
|
-
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if (isFirstRender.current) {
|
|
39
|
-
isFirstRender.current = false
|
|
40
|
-
if (value?.length) validateInput(value)
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (value) setCheckedValues(value)
|
|
45
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- Vue 版(@geckou/ui-vue)同様、外部からの値変更時のみ同期する
|
|
46
|
-
}, [value])
|
|
47
|
-
|
|
48
|
-
const toggleValue = (optionValue: string, checked: boolean) => {
|
|
49
|
-
const newValues = options
|
|
50
|
-
.map((option) => String(option.value))
|
|
51
|
-
.filter((candidate) =>
|
|
52
|
-
candidate === optionValue ? checked : checkedValues.includes(candidate),
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
setCheckedValues(newValues)
|
|
56
|
-
validateInput(newValues)
|
|
57
|
-
onChange?.(newValues)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const errorColor = {
|
|
61
|
-
...(cssStyle?.error ?? {}),
|
|
62
|
-
textColor: COLOR.white,
|
|
63
|
-
backgroundColor: COLOR.red,
|
|
64
|
-
border: {
|
|
65
|
-
color: COLOR.red,
|
|
66
|
-
size: '1px',
|
|
67
|
-
radius: '.25rem',
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<div className="relative flex flex-wrap gap-x-6 gap-y-4">
|
|
73
|
-
{options.map((option) => (
|
|
74
|
-
<LabeledCheckbox
|
|
75
|
-
key={option.value}
|
|
76
|
-
name={name}
|
|
77
|
-
label={option.label}
|
|
78
|
-
checked={checkedValues.includes(String(option.value))}
|
|
79
|
-
onChange={(checked) => toggleValue(String(option.value), checked)}
|
|
80
|
-
isDisabled={isDisabled}
|
|
81
|
-
cssStyle={cssStyle}
|
|
82
|
-
/>
|
|
83
|
-
))}
|
|
84
|
-
<ErrorMessage
|
|
85
|
-
errorMessages={errorMessage ? [errorMessage] : []}
|
|
86
|
-
cssStyle={{
|
|
87
|
-
textColor: errorColor.textColor,
|
|
88
|
-
backgroundColor: errorColor.backgroundColor,
|
|
89
|
-
}}
|
|
90
|
-
/>
|
|
91
|
-
</div>
|
|
92
|
-
)
|
|
93
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { CSSProperties, ReactNode } from 'react'
|
|
4
|
-
import type { CheckBoxStyleForEachStatus } from '../types'
|
|
5
|
-
import { CheckIcon } from './icons/CheckIcon'
|
|
6
|
-
|
|
7
|
-
type Props = {
|
|
8
|
-
name: string
|
|
9
|
-
checked?: boolean
|
|
10
|
-
onChange?: (newValue: boolean) => void
|
|
11
|
-
isDisabled?: boolean
|
|
12
|
-
cssStyle?: CheckBoxStyleForEachStatus
|
|
13
|
-
isDisableAnimation?: boolean
|
|
14
|
-
check?: ReactNode
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function CheckButton({
|
|
18
|
-
name,
|
|
19
|
-
checked,
|
|
20
|
-
onChange,
|
|
21
|
-
isDisabled,
|
|
22
|
-
cssStyle = { default: {} },
|
|
23
|
-
isDisableAnimation,
|
|
24
|
-
check,
|
|
25
|
-
}: Props) {
|
|
26
|
-
const isChecked = checked ?? false
|
|
27
|
-
const currentCssStyle = isDisabled ? cssStyle.disabled : cssStyle.default
|
|
28
|
-
|
|
29
|
-
const style = {
|
|
30
|
-
'--border-color': currentCssStyle?.border?.color || 'blue',
|
|
31
|
-
'--border-size': currentCssStyle?.border?.size || '1px',
|
|
32
|
-
'--radius-size': currentCssStyle?.border?.radius || '0.25rem',
|
|
33
|
-
'--background-color': currentCssStyle?.backgroundColor || '#fff',
|
|
34
|
-
'--duration': isDisableAnimation ? '0s' : '.3s',
|
|
35
|
-
} as CSSProperties
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<>
|
|
39
|
-
<style>
|
|
40
|
-
{
|
|
41
|
-
'@keyframes uiCheckPop{0%{scale:1}10%{scale:.8}50%{scale:1.1}100%{scale:1}}'
|
|
42
|
-
}
|
|
43
|
-
</style>
|
|
44
|
-
<label
|
|
45
|
-
style={style}
|
|
46
|
-
className={`relative flex h-6 w-6 cursor-pointer items-center justify-center rounded-(--radius-size) shadow-[0_0_0_var(--border-size)_var(--border-color)_inset] has-[input:disabled]:pointer-events-none has-[input:disabled]:before:pointer-events-auto has-[input:disabled]:before:absolute has-[input:disabled]:before:inset-0 has-[input:disabled]:before:cursor-not-allowed has-[input:disabled]:before:content-[''] has-[input:focus-visible]:outline-2 has-[input:focus-visible]:outline-offset-2 has-[input:focus-visible]:outline-(--border-color) ${isChecked ? 'animate-[uiCheckPop_var(--duration)_ease-out] bg-(--border-color)' : 'bg-(--background-color)'}`}
|
|
47
|
-
>
|
|
48
|
-
<input
|
|
49
|
-
type="checkbox"
|
|
50
|
-
name={name}
|
|
51
|
-
checked={isChecked}
|
|
52
|
-
disabled={isDisabled}
|
|
53
|
-
onChange={(event) => {
|
|
54
|
-
if (!isDisabled) onChange?.(event.target.checked)
|
|
55
|
-
}}
|
|
56
|
-
className="sr-only"
|
|
57
|
-
/>
|
|
58
|
-
<div
|
|
59
|
-
className={`flex h-4 w-4 items-center justify-center [&>*]:fill-current [&>*]:text-current ${isChecked ? 'text-(--background-color)' : 'text-(--border-color)'}`}
|
|
60
|
-
>
|
|
61
|
-
{check ?? <CheckIcon />}
|
|
62
|
-
</div>
|
|
63
|
-
</label>
|
|
64
|
-
</>
|
|
65
|
-
)
|
|
66
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { CSSProperties } from 'react'
|
|
4
|
-
import type { DateObject, InputBoxStyleForEachStatus } from '../types'
|
|
5
|
-
import {
|
|
6
|
-
MESSAGES,
|
|
7
|
-
composeDateValue,
|
|
8
|
-
formatDateValue,
|
|
9
|
-
splitDate,
|
|
10
|
-
validateDateObject,
|
|
11
|
-
} from '@geckou/ui-core'
|
|
12
|
-
import { useEffect, useRef, useState } from 'react'
|
|
13
|
-
import { InputBox } from './InputBox'
|
|
14
|
-
import { ErrorMessage } from './ErrorMessage'
|
|
15
|
-
import { CalendarIcon } from './icons/CalendarIcon'
|
|
16
|
-
import { COLOR } from '../constants'
|
|
17
|
-
|
|
18
|
-
type Props = {
|
|
19
|
-
name: string
|
|
20
|
-
value?: string
|
|
21
|
-
onChange?: (newValue: string) => void
|
|
22
|
-
cssStyle?: InputBoxStyleForEachStatus
|
|
23
|
-
isDisabled?: boolean
|
|
24
|
-
isRequired?: boolean
|
|
25
|
-
minDate?: string
|
|
26
|
-
maxDate?: string
|
|
27
|
-
size?: 'small' | 'medium'
|
|
28
|
-
type?: 'date' | 'month'
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function DatePicker({
|
|
32
|
-
name,
|
|
33
|
-
value,
|
|
34
|
-
onChange,
|
|
35
|
-
cssStyle,
|
|
36
|
-
isDisabled,
|
|
37
|
-
isRequired,
|
|
38
|
-
minDate = '',
|
|
39
|
-
maxDate = '',
|
|
40
|
-
size = 'medium',
|
|
41
|
-
type = 'date',
|
|
42
|
-
}: Props) {
|
|
43
|
-
const [dateValue, setDateValue] = useState(() =>
|
|
44
|
-
value ? formatDateValue(value, type) : '',
|
|
45
|
-
)
|
|
46
|
-
const [dateObject, setDateObject] = useState<DateObject>(() =>
|
|
47
|
-
value ? splitDate(formatDateValue(value, type)) : { year: '', month: '', day: '' },
|
|
48
|
-
)
|
|
49
|
-
const [errorMessage, setErrorMessage] = useState('')
|
|
50
|
-
|
|
51
|
-
// prop が実際に変わったときだけ同期する(内部編集を上書きしないため)
|
|
52
|
-
const lastValueProp = useRef(value)
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (lastValueProp.current === value) return
|
|
56
|
-
lastValueProp.current = value
|
|
57
|
-
|
|
58
|
-
const normalized = value ? formatDateValue(value, type) : ''
|
|
59
|
-
setDateValue(normalized)
|
|
60
|
-
setDateObject(splitDate(normalized))
|
|
61
|
-
}, [value, type])
|
|
62
|
-
|
|
63
|
-
const validateInput = (newValue: string) => {
|
|
64
|
-
if (!newValue && isRequired)
|
|
65
|
-
{return { isValid: false, message: MESSAGES.required }}
|
|
66
|
-
return { isValid: true, message: '' }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const validateObject = (object: DateObject) =>
|
|
70
|
-
validateDateObject(object, { type, isRequired })
|
|
71
|
-
|
|
72
|
-
const handleDateValueChange = (newValue: string) => {
|
|
73
|
-
setDateValue(newValue)
|
|
74
|
-
setDateObject(splitDate(newValue))
|
|
75
|
-
const { message } = validateInput(newValue)
|
|
76
|
-
setErrorMessage(message)
|
|
77
|
-
onChange?.(newValue)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const handleObjectChange = (key: keyof DateObject, newValue: string) => {
|
|
81
|
-
const next = { ...dateObject, [key]: newValue }
|
|
82
|
-
setDateObject(next)
|
|
83
|
-
const { isValid, message } = validateObject(next)
|
|
84
|
-
setErrorMessage(message)
|
|
85
|
-
|
|
86
|
-
if (isValid) {
|
|
87
|
-
const joined = composeDateValue(next, type)
|
|
88
|
-
setDateValue(joined)
|
|
89
|
-
onChange?.(joined)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const isSmall = size === 'small'
|
|
94
|
-
const textInputClass = `flex-none! ${isSmall ? 'px-[var(--sp-small,0.375rem)]! py-[var(--sp-min,0.1875rem)]! text-[length:var(--fs-small,0.6875rem)]' : 'p-[var(--sp-medium,0.75rem)]!'}`
|
|
95
|
-
const iconStyle = { '--icon-color': COLOR.blue } as CSSProperties
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<InputBox
|
|
99
|
-
cssStyle={cssStyle}
|
|
100
|
-
isDisabled={isDisabled}
|
|
101
|
-
isErrored={!!errorMessage}
|
|
102
|
-
className={`flex w-full items-center leading-none ${isSmall ? 'h-[calc(var(--bv,0.375rem)*5)] px-[var(--sp-min,0.1875rem)]' : 'h-[calc(var(--bv,0.375rem)*6)] px-[var(--sp-small,0.375rem)]'}`}
|
|
103
|
-
>
|
|
104
|
-
<div className="relative flex-none" style={iconStyle}>
|
|
105
|
-
<CalendarIcon
|
|
106
|
-
className={`pointer-events-none absolute inset-y-0 left-[var(--sp-small,0.375rem)] m-auto fill-(--icon-color) ${isSmall ? 'size-[var(--icon-small,0.9375rem)]' : 'size-[var(--icon-medium,1.125rem)]'}`}
|
|
107
|
-
/>
|
|
108
|
-
<input
|
|
109
|
-
type={type}
|
|
110
|
-
name={name}
|
|
111
|
-
value={dateValue}
|
|
112
|
-
max={maxDate}
|
|
113
|
-
min={minDate}
|
|
114
|
-
required={isRequired}
|
|
115
|
-
disabled={isDisabled}
|
|
116
|
-
onChange={(event) => handleDateValueChange(event.target.value)}
|
|
117
|
-
className={`w-[calc(var(--icon-medium,1.125rem)+var(--sp-small,0.375rem)*2)]! cursor-pointer px-[var(--sp-small,0.375rem)]! opacity-0 [&::-webkit-calendar-picker-indicator]:absolute [&::-webkit-calendar-picker-indicator]:left-0 [&::-webkit-calendar-picker-indicator]:h-full [&::-webkit-calendar-picker-indicator]:w-full [&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-0 ${isSmall ? 'py-[var(--sp-min,0.1875rem)]!' : 'py-[var(--sp-medium,0.75rem)]!'}`}
|
|
118
|
-
/>
|
|
119
|
-
</div>
|
|
120
|
-
<input
|
|
121
|
-
value={dateObject.year}
|
|
122
|
-
placeholder="年"
|
|
123
|
-
maxLength={4}
|
|
124
|
-
type="text"
|
|
125
|
-
disabled={isDisabled}
|
|
126
|
-
onChange={(event) => handleObjectChange('year', event.target.value)}
|
|
127
|
-
className={`w-[calc(var(--sp-medium,0.75rem)*2+5ch)]! ${textInputClass}`}
|
|
128
|
-
/>
|
|
129
|
-
/
|
|
130
|
-
<input
|
|
131
|
-
value={dateObject.month}
|
|
132
|
-
placeholder="月"
|
|
133
|
-
maxLength={2}
|
|
134
|
-
type="text"
|
|
135
|
-
disabled={isDisabled}
|
|
136
|
-
onChange={(event) => handleObjectChange('month', event.target.value)}
|
|
137
|
-
className={`w-[calc(var(--sp-medium,0.75rem)*2+3ch)]! ${textInputClass}`}
|
|
138
|
-
/>
|
|
139
|
-
{type === 'date' && <span>/</span>}
|
|
140
|
-
{type === 'date' && (
|
|
141
|
-
<input
|
|
142
|
-
value={dateObject.day}
|
|
143
|
-
placeholder="日"
|
|
144
|
-
maxLength={2}
|
|
145
|
-
type="text"
|
|
146
|
-
disabled={isDisabled}
|
|
147
|
-
onChange={(event) => handleObjectChange('day', event.target.value)}
|
|
148
|
-
className={`w-[calc(var(--sp-medium,0.75rem)*2+3ch)]! ${textInputClass}`}
|
|
149
|
-
/>
|
|
150
|
-
)}
|
|
151
|
-
<ErrorMessage errorMessages={errorMessage ? [errorMessage] : undefined} />
|
|
152
|
-
</InputBox>
|
|
153
|
-
)
|
|
154
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { CSSProperties } from 'react'
|
|
4
|
-
import { useState } from 'react'
|
|
5
|
-
import { DatePicker } from './DatePicker'
|
|
6
|
-
import { COLOR } from '../constants'
|
|
7
|
-
|
|
8
|
-
type DateRange = {
|
|
9
|
-
startDate: string
|
|
10
|
-
endDate: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type Props = {
|
|
14
|
-
isDisabled?: boolean
|
|
15
|
-
onChange?: (newValue: DateRange) => void
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function DateRangePicker({ isDisabled, onChange }: Props) {
|
|
19
|
-
const [startDate, setStartDate] = useState('')
|
|
20
|
-
const [endDate, setEndDate] = useState('')
|
|
21
|
-
|
|
22
|
-
const handleStartChange = (newValue: string) => {
|
|
23
|
-
setStartDate(newValue)
|
|
24
|
-
onChange?.({ startDate: newValue, endDate })
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const handleEndChange = (newValue: string) => {
|
|
28
|
-
setEndDate(newValue)
|
|
29
|
-
onChange?.({ startDate, endDate: newValue })
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const style = {
|
|
33
|
-
'--range-background-color': COLOR.white,
|
|
34
|
-
'--range-border-color': COLOR.gray,
|
|
35
|
-
} as CSSProperties
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<div
|
|
39
|
-
style={style}
|
|
40
|
-
className="flex bg-(--range-background-color) [&>*]:flex-auto [&>*]:rounded-none! [&>*:first-child]:rounded-s-[calc(var(--bv,0.375rem)/2)]! [&>*:last-child]:rounded-e-[calc(var(--bv,0.375rem)/2)]! [&>*:not(:last-child)]:border-e [&>*:not(:last-child)]:border-(--range-border-color)"
|
|
41
|
-
>
|
|
42
|
-
<DatePicker
|
|
43
|
-
value={startDate}
|
|
44
|
-
name="startDate"
|
|
45
|
-
isDisabled={isDisabled}
|
|
46
|
-
onChange={handleStartChange}
|
|
47
|
-
/>
|
|
48
|
-
<div className="flex flex-none! items-center px-[var(--bv,0.375rem)]">
|
|
49
|
-
〜
|
|
50
|
-
</div>
|
|
51
|
-
<DatePicker
|
|
52
|
-
value={endDate}
|
|
53
|
-
name="endDate"
|
|
54
|
-
isDisabled={isDisabled}
|
|
55
|
-
onChange={handleEndChange}
|
|
56
|
-
/>
|
|
57
|
-
</div>
|
|
58
|
-
)
|
|
59
|
-
}
|