@geckou/ui-react 0.1.1 → 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
package/src/components/TabUI.tsx
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { CSSProperties, KeyboardEvent, ReactNode } from 'react'
|
|
4
|
-
import { useId, useRef, useState } from 'react'
|
|
5
|
-
import { COLOR } from '../constants'
|
|
6
|
-
|
|
7
|
-
type Tab = {
|
|
8
|
-
key: string
|
|
9
|
-
label: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
type Props = {
|
|
13
|
-
tabs: Tab[]
|
|
14
|
-
color?: {
|
|
15
|
-
active: string
|
|
16
|
-
background: string
|
|
17
|
-
text: string
|
|
18
|
-
}
|
|
19
|
-
type?: 'tab' | 'button' | 'border'
|
|
20
|
-
initialIndex?: number
|
|
21
|
-
tabSlots?: Record<string, ReactNode>
|
|
22
|
-
panelSlots?: Record<string, ReactNode>
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function TabUI({
|
|
26
|
-
tabs,
|
|
27
|
-
color,
|
|
28
|
-
initialIndex = 0,
|
|
29
|
-
tabSlots,
|
|
30
|
-
panelSlots,
|
|
31
|
-
}: Props) {
|
|
32
|
-
const [activeTab, setActiveTab] = useState(
|
|
33
|
-
() => tabs[initialIndex]?.key ?? tabs[0]?.key ?? '',
|
|
34
|
-
)
|
|
35
|
-
const tabRefs = useRef<Array<HTMLButtonElement | null>>([])
|
|
36
|
-
// 複数インスタンス設置時の DOM id 重複を避ける
|
|
37
|
-
const uid = useId()
|
|
38
|
-
const tabId = (key: string) => `${uid}-tab-${key}`
|
|
39
|
-
const panelId = (key: string) => `${uid}-panel-${key}`
|
|
40
|
-
|
|
41
|
-
const changeTabs = (key: string) => setActiveTab(key)
|
|
42
|
-
|
|
43
|
-
const activateTab = (index: number) => {
|
|
44
|
-
setActiveTab(tabs[index].key)
|
|
45
|
-
tabRefs.current[index]?.focus()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Vue 版(@geckou/ui-vue)は window 全体に keydown を張っていたため、フォーカス位置と
|
|
49
|
-
// 無関係にタブが切り替わり複数設置時に競合した。タブリストにフォーカスが
|
|
50
|
-
// あるときだけ矢印キーで移動する(WAI-ARIA Tabs パターン)
|
|
51
|
-
const handleKeydown = (event: KeyboardEvent<HTMLDivElement>) => {
|
|
52
|
-
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return
|
|
53
|
-
|
|
54
|
-
const currentIndex = tabs.findIndex((tab) => tab.key === activeTab)
|
|
55
|
-
if (currentIndex === -1) return
|
|
56
|
-
|
|
57
|
-
event.preventDefault()
|
|
58
|
-
const lastIndex = tabs.length - 1
|
|
59
|
-
|
|
60
|
-
if (event.key === 'ArrowLeft') {
|
|
61
|
-
activateTab(currentIndex > 0 ? currentIndex - 1 : lastIndex)
|
|
62
|
-
} else {
|
|
63
|
-
activateTab(currentIndex < lastIndex ? currentIndex + 1 : 0)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const style = {
|
|
68
|
-
'--active-color': color?.active || COLOR.blue,
|
|
69
|
-
'--background-color': color?.background || 'transparent',
|
|
70
|
-
} as CSSProperties
|
|
71
|
-
|
|
72
|
-
return (
|
|
73
|
-
<div>
|
|
74
|
-
<div
|
|
75
|
-
style={style}
|
|
76
|
-
className="flex bg-(--background-color)"
|
|
77
|
-
role="tablist"
|
|
78
|
-
onKeyDown={handleKeydown}
|
|
79
|
-
>
|
|
80
|
-
{tabs.map((tab, index) => (
|
|
81
|
-
<button
|
|
82
|
-
key={tab.key}
|
|
83
|
-
id={tabId(tab.key)}
|
|
84
|
-
ref={(el) => {
|
|
85
|
-
tabRefs.current[index] = el
|
|
86
|
-
}}
|
|
87
|
-
type="button"
|
|
88
|
-
role="tab"
|
|
89
|
-
aria-controls={panelId(tab.key)}
|
|
90
|
-
aria-selected={activeTab === tab.key}
|
|
91
|
-
tabIndex={activeTab === tab.key ? 0 : -1}
|
|
92
|
-
className={`border-none bg-transparent px-4 py-2 text-base ${activeTab === tab.key ? 'cursor-auto' : 'cursor-pointer'}`}
|
|
93
|
-
onClick={() => changeTabs(tab.key)}
|
|
94
|
-
>
|
|
95
|
-
{tabSlots?.[tab.key] ?? tab.label}
|
|
96
|
-
</button>
|
|
97
|
-
))}
|
|
98
|
-
</div>
|
|
99
|
-
{tabs.map((tab) => (
|
|
100
|
-
<div
|
|
101
|
-
key={`${tab.key}_panel`}
|
|
102
|
-
id={panelId(tab.key)}
|
|
103
|
-
role="tabpanel"
|
|
104
|
-
aria-labelledby={tabId(tab.key)}
|
|
105
|
-
hidden={activeTab !== tab.key}
|
|
106
|
-
>
|
|
107
|
-
{panelSlots?.[`${tab.key}Contents`]}
|
|
108
|
-
</div>
|
|
109
|
-
))}
|
|
110
|
-
</div>
|
|
111
|
-
)
|
|
112
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { InputBoxStyleForEachStatus, Validates } from '../types'
|
|
4
|
-
import { isEmptyValue, validateInputValue } from '@geckou/ui-core'
|
|
5
|
-
import { useEffect, useRef, useState } from 'react'
|
|
6
|
-
import { InputBox } from './InputBox'
|
|
7
|
-
import { ErrorMessage } from './ErrorMessage'
|
|
8
|
-
|
|
9
|
-
type InputValue = string | null
|
|
10
|
-
|
|
11
|
-
type Props = {
|
|
12
|
-
name: string
|
|
13
|
-
value?: InputValue
|
|
14
|
-
onChange?: (newValue: InputValue) => void
|
|
15
|
-
cssStyle?: InputBoxStyleForEachStatus
|
|
16
|
-
placeholder?: string
|
|
17
|
-
isDisabled?: boolean
|
|
18
|
-
isRequired?: boolean
|
|
19
|
-
rows?: number
|
|
20
|
-
maxLength?: number
|
|
21
|
-
autocomplete?: string
|
|
22
|
-
validates?: Validates
|
|
23
|
-
autoAdjustHeight?: boolean
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function TextArea({
|
|
27
|
-
name,
|
|
28
|
-
value,
|
|
29
|
-
onChange,
|
|
30
|
-
cssStyle,
|
|
31
|
-
placeholder = '入力してください',
|
|
32
|
-
isDisabled,
|
|
33
|
-
isRequired,
|
|
34
|
-
rows,
|
|
35
|
-
maxLength = 100,
|
|
36
|
-
autocomplete = 'off',
|
|
37
|
-
validates = [],
|
|
38
|
-
autoAdjustHeight,
|
|
39
|
-
}: Props) {
|
|
40
|
-
const [errorMessages, setErrorMessages] = useState<string[]>([])
|
|
41
|
-
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
|
42
|
-
const inputValue = value ?? ''
|
|
43
|
-
|
|
44
|
-
const validateValue = () => {
|
|
45
|
-
setErrorMessages(validateInputValue(inputValue, { isRequired, validates }))
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Vue 版(@geckou/ui-vue)の watch(immediate: !!modelValue) と等価:
|
|
49
|
-
// 初期値ありならマウント時にも検証、以後は値が変化したときのみ検証
|
|
50
|
-
const initialValue = useRef(inputValue)
|
|
51
|
-
const hasChanged = useRef(false)
|
|
52
|
-
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (autoAdjustHeight && textareaRef.current) {
|
|
55
|
-
textareaRef.current.style.height = 'auto'
|
|
56
|
-
textareaRef.current.style.height = `calc(${textareaRef.current.scrollHeight}px - 2rem)`
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!hasChanged.current) {
|
|
60
|
-
if (inputValue === initialValue.current) {
|
|
61
|
-
// 数値 0 も初期値として扱うため truthy 判定は使わない
|
|
62
|
-
if (!isEmptyValue(inputValue)) validateValue()
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
hasChanged.current = true
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
validateValue()
|
|
69
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- Vue 版(@geckou/ui-vue)同様、値の変化時のみ検証・高さ調整する
|
|
70
|
-
}, [inputValue])
|
|
71
|
-
|
|
72
|
-
return (
|
|
73
|
-
<InputBox
|
|
74
|
-
cssStyle={cssStyle}
|
|
75
|
-
className="min-h-[4em]"
|
|
76
|
-
isErrored={!!errorMessages.length}
|
|
77
|
-
isDisabled={isDisabled}
|
|
78
|
-
>
|
|
79
|
-
<textarea
|
|
80
|
-
ref={textareaRef}
|
|
81
|
-
name={name}
|
|
82
|
-
value={inputValue}
|
|
83
|
-
required={isRequired}
|
|
84
|
-
placeholder={placeholder}
|
|
85
|
-
disabled={isDisabled}
|
|
86
|
-
autoComplete={autocomplete}
|
|
87
|
-
rows={rows}
|
|
88
|
-
maxLength={maxLength}
|
|
89
|
-
aria-invalid={errorMessages.length ? 'true' : undefined}
|
|
90
|
-
onChange={(event) => onChange?.(event.target.value)}
|
|
91
|
-
onBlur={() => validateValue()}
|
|
92
|
-
/>
|
|
93
|
-
<ErrorMessage
|
|
94
|
-
errorMessages={errorMessages}
|
|
95
|
-
cssStyle={{
|
|
96
|
-
textColor: cssStyle?.error?.backgroundColor,
|
|
97
|
-
backgroundColor: cssStyle?.error?.textColor,
|
|
98
|
-
}}
|
|
99
|
-
/>
|
|
100
|
-
</InputBox>
|
|
101
|
-
)
|
|
102
|
-
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from 'react'
|
|
4
|
-
import type { InputBoxStyleForEachStatus, Validates } from '../types'
|
|
5
|
-
import {
|
|
6
|
-
convertFullWidthToHalfWidth,
|
|
7
|
-
isEmptyValue,
|
|
8
|
-
validateInputValue,
|
|
9
|
-
} from '@geckou/ui-core'
|
|
10
|
-
import { useEffect, useRef, useState } from 'react'
|
|
11
|
-
import { InputBox } from './InputBox'
|
|
12
|
-
import { ErrorMessage } from './ErrorMessage'
|
|
13
|
-
|
|
14
|
-
type InputValue = string | number
|
|
15
|
-
|
|
16
|
-
type Props = {
|
|
17
|
-
name: string
|
|
18
|
-
value?: InputValue
|
|
19
|
-
onChange?: (newValue: InputValue) => void
|
|
20
|
-
cssStyle?: InputBoxStyleForEachStatus
|
|
21
|
-
inputType?: string
|
|
22
|
-
placeholder?: string
|
|
23
|
-
isDisabled?: boolean
|
|
24
|
-
isRequired?: boolean
|
|
25
|
-
maxLength?: number
|
|
26
|
-
autocomplete?: string
|
|
27
|
-
validates?: Validates
|
|
28
|
-
before?: ReactNode
|
|
29
|
-
after?: ReactNode
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function TextBox({
|
|
33
|
-
name,
|
|
34
|
-
value,
|
|
35
|
-
onChange,
|
|
36
|
-
cssStyle,
|
|
37
|
-
inputType = 'text',
|
|
38
|
-
placeholder = '入力してください',
|
|
39
|
-
isDisabled,
|
|
40
|
-
isRequired,
|
|
41
|
-
maxLength = 30,
|
|
42
|
-
autocomplete = 'off',
|
|
43
|
-
validates = [],
|
|
44
|
-
before,
|
|
45
|
-
after,
|
|
46
|
-
}: Props) {
|
|
47
|
-
const [errorMessages, setErrorMessages] = useState<string[]>([])
|
|
48
|
-
const inputValue = value ?? ''
|
|
49
|
-
|
|
50
|
-
const validateValue = () => {
|
|
51
|
-
setErrorMessages(validateInputValue(inputValue, { isRequired, validates }))
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Vue 版(@geckou/ui-vue)の watch(immediate: !!modelValue) と等価:
|
|
55
|
-
// 初期値ありならマウント時にも検証、以後は値が変化したときのみ検証
|
|
56
|
-
const initialValue = useRef(inputValue)
|
|
57
|
-
const hasChanged = useRef(false)
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
if (!hasChanged.current) {
|
|
61
|
-
if (inputValue === initialValue.current) {
|
|
62
|
-
if (!isEmptyValue(inputValue)) validateValue()
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
hasChanged.current = true
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
validateValue()
|
|
69
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- Vue 版(@geckou/ui-vue)同様、値の変化時のみ検証する
|
|
70
|
-
}, [inputValue])
|
|
71
|
-
|
|
72
|
-
const isComposing = useRef(false)
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<InputBox
|
|
76
|
-
cssStyle={cssStyle}
|
|
77
|
-
className="inline-flex [&>input]:flex-auto"
|
|
78
|
-
isErrored={!!errorMessages.length}
|
|
79
|
-
isDisabled={isDisabled}
|
|
80
|
-
>
|
|
81
|
-
{before}
|
|
82
|
-
<input
|
|
83
|
-
type={inputType}
|
|
84
|
-
name={name}
|
|
85
|
-
value={inputValue}
|
|
86
|
-
required={isRequired}
|
|
87
|
-
placeholder={placeholder}
|
|
88
|
-
disabled={isDisabled}
|
|
89
|
-
autoComplete={autocomplete}
|
|
90
|
-
maxLength={maxLength}
|
|
91
|
-
aria-invalid={errorMessages.length ? 'true' : undefined}
|
|
92
|
-
onChange={(event) => {
|
|
93
|
-
// IME 変換中に全角→半角変換すると未確定文字列が壊れるため、確定後に変換する
|
|
94
|
-
const rawValue = event.target.value
|
|
95
|
-
onChange?.(
|
|
96
|
-
isComposing.current
|
|
97
|
-
? rawValue
|
|
98
|
-
: convertFullWidthToHalfWidth(rawValue),
|
|
99
|
-
)
|
|
100
|
-
}}
|
|
101
|
-
onCompositionStart={() => {
|
|
102
|
-
isComposing.current = true
|
|
103
|
-
}}
|
|
104
|
-
onCompositionEnd={(event) => {
|
|
105
|
-
isComposing.current = false
|
|
106
|
-
onChange?.(convertFullWidthToHalfWidth(event.currentTarget.value))
|
|
107
|
-
}}
|
|
108
|
-
onBlur={() => validateValue()}
|
|
109
|
-
/>
|
|
110
|
-
{after}
|
|
111
|
-
<ErrorMessage
|
|
112
|
-
errorMessages={errorMessages}
|
|
113
|
-
cssStyle={{
|
|
114
|
-
textColor: cssStyle?.error?.backgroundColor,
|
|
115
|
-
backgroundColor: cssStyle?.error?.textColor,
|
|
116
|
-
}}
|
|
117
|
-
/>
|
|
118
|
-
</InputBox>
|
|
119
|
-
)
|
|
120
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { CSSProperties } from 'react'
|
|
4
|
-
import type { BaseStyle, StyleForEachStatus } from '../types'
|
|
5
|
-
import { COLOR } from '../constants'
|
|
6
|
-
|
|
7
|
-
type ToggleStyle = {
|
|
8
|
-
on: BaseStyle
|
|
9
|
-
off: BaseStyle
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
type Props = {
|
|
13
|
-
name: string
|
|
14
|
-
label?: Record<'on' | 'off', string>
|
|
15
|
-
checked?: boolean
|
|
16
|
-
onChange?: (newValue: boolean) => void
|
|
17
|
-
isDisabled?: boolean
|
|
18
|
-
cssStyle?: StyleForEachStatus<ToggleStyle>
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function ToggleButton({
|
|
22
|
-
name,
|
|
23
|
-
label = { on: 'ON', off: 'OFF' },
|
|
24
|
-
checked,
|
|
25
|
-
onChange,
|
|
26
|
-
isDisabled,
|
|
27
|
-
cssStyle,
|
|
28
|
-
}: Props) {
|
|
29
|
-
const isChecked = checked ?? false
|
|
30
|
-
const maxTextLength = Math.max(label.on.length, label.off.length)
|
|
31
|
-
const baseStyle = isDisabled ? cssStyle?.disabled : cssStyle?.default
|
|
32
|
-
|
|
33
|
-
const currentCssStyle = {
|
|
34
|
-
on: {
|
|
35
|
-
textColor: isDisabled ? COLOR.lightGray : COLOR.white,
|
|
36
|
-
backgroundColor: isDisabled ? COLOR.gray : COLOR.blue,
|
|
37
|
-
border: {
|
|
38
|
-
color: isDisabled ? COLOR.gray : COLOR.blue,
|
|
39
|
-
size: '1px',
|
|
40
|
-
radius: '.25rem',
|
|
41
|
-
},
|
|
42
|
-
boxShadow: '0 0 0 0 rgba(0, 0, 0, 0)',
|
|
43
|
-
...baseStyle?.on,
|
|
44
|
-
},
|
|
45
|
-
off: {
|
|
46
|
-
textColor: isDisabled ? COLOR.gray : COLOR.white,
|
|
47
|
-
backgroundColor: isDisabled ? COLOR.lightGray : COLOR.darkGray,
|
|
48
|
-
border: {
|
|
49
|
-
color: isDisabled ? COLOR.gray : COLOR.darkGray,
|
|
50
|
-
size: '1px',
|
|
51
|
-
radius: '.25rem',
|
|
52
|
-
},
|
|
53
|
-
boxShadow: '0 0 0 0 rgba(0, 0, 0, 0)',
|
|
54
|
-
...baseStyle?.off,
|
|
55
|
-
},
|
|
56
|
-
}[isChecked ? 'on' : 'off']
|
|
57
|
-
|
|
58
|
-
const style = {
|
|
59
|
-
'--text-color': currentCssStyle.textColor,
|
|
60
|
-
'--border-color': currentCssStyle.border?.color,
|
|
61
|
-
'--border-size': currentCssStyle.border?.size,
|
|
62
|
-
'--radius-size': currentCssStyle.border?.radius,
|
|
63
|
-
'--background-color': currentCssStyle.backgroundColor,
|
|
64
|
-
'--box-shadow': currentCssStyle.boxShadow,
|
|
65
|
-
'--inline-size': `${maxTextLength * 2}ch`,
|
|
66
|
-
'--handle-size': '1.5rem',
|
|
67
|
-
'--padding-size': 'calc(var(--border-size) + 2px)',
|
|
68
|
-
'--duration': '.15s',
|
|
69
|
-
} as CSSProperties
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<button
|
|
73
|
-
type="button"
|
|
74
|
-
style={style}
|
|
75
|
-
disabled={isDisabled}
|
|
76
|
-
aria-pressed={isChecked}
|
|
77
|
-
aria-label={name}
|
|
78
|
-
onClick={(event) => {
|
|
79
|
-
event.stopPropagation()
|
|
80
|
-
if (!isDisabled) onChange?.(!isChecked)
|
|
81
|
-
}}
|
|
82
|
-
className="relative inline-block w-[calc(var(--inline-size)+var(--handle-size)+(var(--padding-size)*2))] cursor-pointer rounded-(--radius-size) border-none bg-(--background-color) p-(--padding-size) shadow-[0_0_0_var(--border-size)_var(--border-color)_inset,var(--box-shadow)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--border-color)"
|
|
83
|
-
>
|
|
84
|
-
<input
|
|
85
|
-
type="checkbox"
|
|
86
|
-
name={name}
|
|
87
|
-
checked={isChecked}
|
|
88
|
-
disabled={isDisabled}
|
|
89
|
-
readOnly
|
|
90
|
-
className="hidden"
|
|
91
|
-
/>
|
|
92
|
-
<div
|
|
93
|
-
data-on={label.on}
|
|
94
|
-
data-off={label.off}
|
|
95
|
-
className={`absolute top-0 left-0 h-full w-full text-(--text-color) uppercase before:absolute before:right-0 before:m-auto before:inline-flex before:h-full before:w-[calc(100%-var(--handle-size)-var(--padding-size))] before:items-center before:justify-center before:leading-none before:transition-opacity before:duration-(--duration) before:ease-out before:content-[attr(data-off)] after:absolute after:left-0 after:m-auto after:inline-flex after:h-full after:w-[calc(100%-var(--handle-size)-var(--padding-size))] after:items-center after:justify-center after:leading-none after:transition-opacity after:duration-(--duration) after:ease-out after:content-[attr(data-on)] ${isChecked ? 'before:opacity-0 after:opacity-100' : 'before:opacity-100 after:opacity-0'}`}
|
|
96
|
-
/>
|
|
97
|
-
<div
|
|
98
|
-
className={`relative m-0 aspect-square w-(--handle-size) rounded-[calc(var(--radius-size)-(var(--padding-size)/2))] bg-(--text-color) shadow-(--box-shadow) transition-[left] duration-(--duration) ease-out ${isChecked ? 'left-[calc(100%-var(--handle-size))]' : 'left-0'}`}
|
|
99
|
-
/>
|
|
100
|
-
</button>
|
|
101
|
-
)
|
|
102
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
type Props = {
|
|
2
|
-
className?: string
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function BackupIcon({ className }: Props) {
|
|
6
|
-
return (
|
|
7
|
-
<svg
|
|
8
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
-
viewBox="0 -960 960 960"
|
|
10
|
-
fill="currentColor"
|
|
11
|
-
className={className}
|
|
12
|
-
>
|
|
13
|
-
<path d="M260-160q-91 0-155.5-63T40-377q0-78 47-139t123-78q25-92 100-149t170-57q117 0 198.5 81.5T760-520q69 8 114.5 59.5T920-340q0 75-52.5 127.5T740-160H520q-33 0-56.5-23.5T440-240v-206l-64 62-56-56 160-160 160 160-56 56-64-62v206h220q42 0 71-29t29-71q0-42-29-71t-71-29h-60v-80q0-83-58.5-141.5T480-720q-83 0-141.5 58.5T280-520h-20q-58 0-99 41t-41 99q0 58 41 99t99 41h100v80H260Z" />
|
|
14
|
-
</svg>
|
|
15
|
-
)
|
|
16
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
type Props = {
|
|
2
|
-
className?: string
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function CalendarIcon({ className }: Props) {
|
|
6
|
-
return (
|
|
7
|
-
<svg
|
|
8
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
-
viewBox="0 -960 960 960"
|
|
10
|
-
className={className}
|
|
11
|
-
>
|
|
12
|
-
<path d="M200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v560q0 33-23.5 56.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Z" />
|
|
13
|
-
</svg>
|
|
14
|
-
)
|
|
15
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
type Props = {
|
|
2
|
-
className?: string
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function CloseIcon({ className }: Props) {
|
|
6
|
-
return (
|
|
7
|
-
<svg
|
|
8
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
-
viewBox="0 -960 960 960"
|
|
10
|
-
fill="currentColor"
|
|
11
|
-
className={className}
|
|
12
|
-
>
|
|
13
|
-
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" />
|
|
14
|
-
</svg>
|
|
15
|
-
)
|
|
16
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
type Props = {
|
|
2
|
-
className?: string
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function KeyboardArrowDownIcon({ className }: Props) {
|
|
6
|
-
return (
|
|
7
|
-
<svg
|
|
8
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
-
viewBox="0 -960 960 960"
|
|
10
|
-
fill="currentColor"
|
|
11
|
-
className={className}
|
|
12
|
-
>
|
|
13
|
-
<path d="M480-344 240-584l56-56 184 184 184-184 56 56-240 240Z" />
|
|
14
|
-
</svg>
|
|
15
|
-
)
|
|
16
|
-
}
|
package/src/constants/index.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useEffect, useMemo, useSyncExternalStore } from 'react'
|
|
4
|
-
import { createFormValidationStore } from '@geckou/ui-core'
|
|
5
|
-
import type { FormValidationStore } from '@geckou/ui-core'
|
|
6
|
-
|
|
7
|
-
export type UseFormValidationResult = {
|
|
8
|
-
/** 登録済みの入力がすべて有効かどうか */
|
|
9
|
-
isAllValid: boolean
|
|
10
|
-
/** 無効になっている入力の name 一覧 */
|
|
11
|
-
invalidNames: string[]
|
|
12
|
-
/** 入力の状態を登録・更新する */
|
|
13
|
-
setValid: FormValidationStore['setValid']
|
|
14
|
-
/** 管理対象から外す */
|
|
15
|
-
remove: FormValidationStore['remove']
|
|
16
|
-
/** すべての状態を破棄する */
|
|
17
|
-
reset: FormValidationStore['reset']
|
|
18
|
-
/** 子コンポーネントへ直接渡すためのストア本体 */
|
|
19
|
-
store: FormValidationStore
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* フォーム内の各入力のバリデーション状態をまとめて管理する。
|
|
24
|
-
*
|
|
25
|
-
* 状態そのものは @geckou/ui-core の createFormValidationStore が持つため、
|
|
26
|
-
* 判定ロジックは Vue 実装(@geckou/ui-vue の FormValidationManager)と共有される。
|
|
27
|
-
*
|
|
28
|
-
* ```tsx
|
|
29
|
-
* const { isAllValid, store } = useFormValidation()
|
|
30
|
-
* // <DatePicker name="startedOn" formValidationStore={store} />
|
|
31
|
-
* <button disabled={!isAllValid}>送信</button>
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
export function useFormValidation(): UseFormValidationResult {
|
|
35
|
-
const store = useMemo(() => createFormValidationStore(), [])
|
|
36
|
-
const snapshot = useSyncExternalStore(
|
|
37
|
-
store.subscribe,
|
|
38
|
-
store.getSnapshot,
|
|
39
|
-
// SSR 時は登録がまだ無いので、すべて有効なスナップショットを返す
|
|
40
|
-
store.getSnapshot,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
isAllValid: snapshot.isAllValid,
|
|
45
|
-
invalidNames: snapshot.invalidNames,
|
|
46
|
-
setValid: store.setValid,
|
|
47
|
-
remove: store.remove,
|
|
48
|
-
reset: store.reset,
|
|
49
|
-
store,
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 入力コンポーネント側から検証結果をストアへ通知する。
|
|
55
|
-
* アンマウント時には登録を解除し、無効判定が残らないようにする。
|
|
56
|
-
*/
|
|
57
|
-
export function useRegisterValidation(
|
|
58
|
-
store: FormValidationStore | null | undefined,
|
|
59
|
-
name: string,
|
|
60
|
-
isValid: boolean,
|
|
61
|
-
): void {
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
store?.setValid(name, isValid)
|
|
64
|
-
}, [store, name, isValid])
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
return () => store?.remove(name)
|
|
68
|
-
}, [store, name])
|
|
69
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
export { BasicButton } from './components/BasicButton'
|
|
2
|
-
export { CheckBox } from './components/CheckBox'
|
|
3
|
-
export { CheckBoxes } from './components/CheckBoxes'
|
|
4
|
-
export { CheckButton } from './components/CheckButton'
|
|
5
|
-
export { DatePicker } from './components/DatePicker'
|
|
6
|
-
export { DateRangePicker } from './components/DateRangePicker'
|
|
7
|
-
export { DateSelector } from './components/DateSelector'
|
|
8
|
-
export { DropdownUi } from './components/DropdownUi'
|
|
9
|
-
export type { DropdownUiHandle } from './components/DropdownUi'
|
|
10
|
-
export { ErrorMessage } from './components/ErrorMessage'
|
|
11
|
-
export { FileInput } from './components/FileInput'
|
|
12
|
-
export { InputBox } from './components/InputBox'
|
|
13
|
-
export { InputGroup } from './components/InputGroup'
|
|
14
|
-
export { LabeledCheckbox } from './components/LabeledCheckbox'
|
|
15
|
-
export { LabeledFieldset } from './components/LabeledFieldset'
|
|
16
|
-
export { LoadingSpinner } from './components/LoadingSpinner'
|
|
17
|
-
export { ModalBox } from './components/ModalBox'
|
|
18
|
-
export { PopupBox } from './components/PopupBox'
|
|
19
|
-
export type { PopupBoxHandle } from './components/PopupBox'
|
|
20
|
-
export { RadioButtons } from './components/RadioButtons'
|
|
21
|
-
export { SearchableSelectBox } from './components/SearchableSelectBox'
|
|
22
|
-
export { SelectBox } from './components/SelectBox'
|
|
23
|
-
export { SlideDownUi } from './components/SlideDownUi'
|
|
24
|
-
export type { SlideDownUiHandle } from './components/SlideDownUi'
|
|
25
|
-
export { TabUI } from './components/TabUI'
|
|
26
|
-
export { TextArea } from './components/TextArea'
|
|
27
|
-
export { TextBox } from './components/TextBox'
|
|
28
|
-
export { ToggleButton } from './components/ToggleButton'
|
|
29
|
-
|
|
30
|
-
export { BackupIcon } from './components/icons/BackupIcon'
|
|
31
|
-
export { CheckIcon } from './components/icons/CheckIcon'
|
|
32
|
-
export { CloseIcon } from './components/icons/CloseIcon'
|
|
33
|
-
export { CalendarIcon } from './components/icons/CalendarIcon'
|
|
34
|
-
export { KeyboardArrowDownIcon } from './components/icons/KeyboardArrowDownIcon'
|
|
35
|
-
|
|
36
|
-
export {
|
|
37
|
-
useFormValidation,
|
|
38
|
-
useRegisterValidation,
|
|
39
|
-
} from './hooks/useFormValidation'
|
|
40
|
-
export type { UseFormValidationResult } from './hooks/useFormValidation'
|
|
41
|
-
|
|
42
|
-
export { COLOR, BORDER, MESSAGES, INPUT_BOX_DEFAULT_STYLES } from './constants'
|
|
43
|
-
export type {
|
|
44
|
-
StateVariation,
|
|
45
|
-
BorderStyle,
|
|
46
|
-
BaseStyle,
|
|
47
|
-
StyleForEachStatus,
|
|
48
|
-
InputBoxStyle,
|
|
49
|
-
InputBoxStyleForEachStatus,
|
|
50
|
-
ButtonStyle,
|
|
51
|
-
ButtonStyleForEachStatus,
|
|
52
|
-
CheckBoxStyle,
|
|
53
|
-
CheckBoxStyleForEachStatus,
|
|
54
|
-
RadioButtonStyle,
|
|
55
|
-
RadioButtonStyleForEachStatus,
|
|
56
|
-
SelectValue,
|
|
57
|
-
Option,
|
|
58
|
-
Validate,
|
|
59
|
-
Validates,
|
|
60
|
-
InputValue,
|
|
61
|
-
DateObject,
|
|
62
|
-
DateType,
|
|
63
|
-
ValidationResult,
|
|
64
|
-
} from './types'
|
|
65
|
-
|
|
66
|
-
export type { FormValidationStore } from '@geckou/ui-core'
|
package/src/types/index.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// フォーム部品の型は @geckou/ui-core が正。Vue / React で共通のため、ここでは再エクスポートのみ行う
|
|
2
|
-
export type {
|
|
3
|
-
StateVariation,
|
|
4
|
-
BorderStyle,
|
|
5
|
-
BaseStyle,
|
|
6
|
-
StyleForEachStatus,
|
|
7
|
-
InputBoxStyle,
|
|
8
|
-
InputBoxStyleForEachStatus,
|
|
9
|
-
ButtonStyle,
|
|
10
|
-
ButtonStyleForEachStatus,
|
|
11
|
-
CheckBoxStyle,
|
|
12
|
-
CheckBoxStyleForEachStatus,
|
|
13
|
-
RadioButtonStyle,
|
|
14
|
-
RadioButtonStyleForEachStatus,
|
|
15
|
-
SelectValue,
|
|
16
|
-
Option,
|
|
17
|
-
Validate,
|
|
18
|
-
Validates,
|
|
19
|
-
InputValue,
|
|
20
|
-
DateObject,
|
|
21
|
-
DateType,
|
|
22
|
-
ValidationResult,
|
|
23
|
-
} from '@geckou/ui-core'
|