@datability/8ui 1.0.0 → 1.1.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.
Files changed (84) hide show
  1. package/.prettierrc +8 -0
  2. package/.vscode/extensions.json +6 -0
  3. package/README.md +1 -3
  4. package/declaration.d.ts +10 -0
  5. package/docker-compose.yml +20 -0
  6. package/eslint.config.js +23 -0
  7. package/{dist/index.html → index.html} +1 -2
  8. package/package.json +13 -14
  9. package/src/App.tsx +370 -0
  10. package/src/components/assets/closed.svg +4 -0
  11. package/src/components/assets/expand-arrow.svg +3 -0
  12. package/src/components/assets/visibility-off.svg +5 -0
  13. package/src/components/assets/visibility.svg +5 -0
  14. package/src/components/blackdrop/index.scss +16 -0
  15. package/src/components/blackdrop/index.tsx +18 -0
  16. package/src/components/blackdrop/index.type.ts +7 -0
  17. package/src/components/button/index.scss +40 -0
  18. package/src/components/button/index.tsx +44 -0
  19. package/src/components/button/index.type.ts +13 -0
  20. package/src/components/chip/index.scss +32 -0
  21. package/src/components/chip/index.tsx +39 -0
  22. package/src/components/chip/index.type.ts +12 -0
  23. package/src/components/context.tsx +26 -0
  24. package/src/components/divider/index.scss +4 -0
  25. package/src/components/divider/index.tsx +13 -0
  26. package/src/components/index.ts +62 -0
  27. package/src/components/input/extend.scss +19 -0
  28. package/src/components/input/input-auto-complete/index.scss +53 -0
  29. package/src/components/input/input-auto-complete/index.tsx +140 -0
  30. package/src/components/input/input-auto-complete/index.type.tsx +13 -0
  31. package/src/components/input/input-base/index.scss +14 -0
  32. package/src/components/input/input-base/index.tsx +39 -0
  33. package/src/components/input/input-base/index.type.tsx +13 -0
  34. package/src/components/input/input-basic/index.scss +9 -0
  35. package/src/components/input/input-basic/index.tsx +47 -0
  36. package/src/components/input/input-basic/index.type.tsx +8 -0
  37. package/src/components/input/input-checkbox/index.scss +30 -0
  38. package/src/components/input/input-checkbox/index.tsx +69 -0
  39. package/src/components/input/input-checkbox/index.type.tsx +11 -0
  40. package/src/components/input/input-date/index.scss +102 -0
  41. package/src/components/input/input-date/index.tsx +354 -0
  42. package/src/components/input/input-date/index.type.tsx +11 -0
  43. package/src/components/input/input-date-range/index.scss +101 -0
  44. package/src/components/input/input-date-range/index.tsx +284 -0
  45. package/src/components/input/input-date-range/index.type.tsx +11 -0
  46. package/src/components/input/input-date-time/index.scss +179 -0
  47. package/src/components/input/input-date-time/index.tsx +367 -0
  48. package/src/components/input/input-date-time/index.type.tsx +11 -0
  49. package/src/components/input/input-number/index.scss +9 -0
  50. package/src/components/input/input-number/index.tsx +118 -0
  51. package/src/components/input/input-number/index.type.tsx +11 -0
  52. package/src/components/input/input-password/index.scss +22 -0
  53. package/src/components/input/input-password/index.tsx +60 -0
  54. package/src/components/input/input-password/index.type.tsx +8 -0
  55. package/src/components/input/input-radio/index.scss +35 -0
  56. package/src/components/input/input-radio/index.tsx +72 -0
  57. package/src/components/input/input-radio/index.type.tsx +12 -0
  58. package/src/components/input/input-select/index.scss +81 -0
  59. package/src/components/input/input-select/index.tsx +113 -0
  60. package/src/components/input/input-select/index.type.tsx +15 -0
  61. package/src/components/input/input-switch/index.scss +84 -0
  62. package/src/components/input/input-switch/index.tsx +44 -0
  63. package/src/components/input/input-switch/index.type.tsx +4 -0
  64. package/src/components/input/input-textarea/index.scss +10 -0
  65. package/src/components/input/input-textarea/index.tsx +48 -0
  66. package/src/components/input/input-textarea/index.type.tsx +10 -0
  67. package/src/components/menu/index.scss +30 -0
  68. package/src/components/menu/index.tsx +136 -0
  69. package/src/components/menu/index.type.ts +8 -0
  70. package/src/components/modal/index.scss +33 -0
  71. package/src/components/modal/index.tsx +99 -0
  72. package/src/components/modal/index.type.tsx +8 -0
  73. package/src/index.scss +44 -0
  74. package/src/index.ts +62 -0
  75. package/src/logoDownload.svg +3 -0
  76. package/src/main.tsx +9 -0
  77. package/tsconfig.app.json +28 -0
  78. package/tsconfig.json +42 -0
  79. package/tsconfig.node.json +29 -0
  80. package/vite.config.d.ts +2 -0
  81. package/vite.config.ts +35 -0
  82. package/dist/assets/index-BYmsRLQS.js +0 -48
  83. package/dist/assets/index-QxCDX2bt.css +0 -1
  84. /package/{dist → public}/vite.svg +0 -0
@@ -0,0 +1,367 @@
1
+ // Lib
2
+ import React, { useEffect, useRef, useState } from "react"
3
+ import { Controller, useFormContext } from "react-hook-form"
4
+
5
+ // Images
6
+ import expandArrowSVG from "../../assets/expand-arrow.svg"
7
+ import closedSVG from "../../assets/closed.svg"
8
+
9
+ // Include in project
10
+ import "./index.scss"
11
+ import InputBase from "../input-base"
12
+ import type { PropsInputDateTime } from "./index.type"
13
+ import Menu from "../../menu"
14
+ import Modal from "../../modal"
15
+ import Button from "../../button"
16
+ import { createPortal } from "react-dom"
17
+ import {
18
+ generateMonthOptions,
19
+ generateYearOptions,
20
+ getDateNow,
21
+ getDayInDateString,
22
+ getDaysInMonthWithWeekday,
23
+ getMonthInDateString,
24
+ getYearInDateString,
25
+ updateMonthInDateString,
26
+ updateYearInDateString,
27
+ } from "../input-date"
28
+
29
+ const InputDateTime: React.FC<PropsInputDateTime> = ({
30
+ name,
31
+ label,
32
+ placeholder,
33
+ disabled = false,
34
+ require = false,
35
+ fullWidth = false,
36
+ isHideClearIcon = true,
37
+ maxYear,
38
+ minYear,
39
+ }) => {
40
+ const { control } = useFormContext()
41
+
42
+ const hourWrapperRef = useRef<HTMLDivElement>(null)
43
+ const minWrapperRef = useRef<HTMLDivElement>(null)
44
+
45
+ const [isOpenModal, setIsOpenModal] = useState(false)
46
+
47
+ const [showDay, setShowDay] = useState(getDateNow())
48
+ const [selectedDay, setSelectedDay] = useState(getDateNow())
49
+ const [selectedHour, setSelectedHour] = useState("00")
50
+ const [selectedMin, setSelectedMin] = useState("00")
51
+
52
+ const yearOption = generateYearOptions(minYear, maxYear)
53
+ const monthOption = generateMonthOptions()
54
+ const hourOption = generateHourOptions()
55
+ const minOption = generateMinuteOptions()
56
+
57
+ useEffect(() => {
58
+ function scrollToChecked(wrapper: HTMLDivElement | null) {
59
+ if (!wrapper) return
60
+ const checkedEl = wrapper.querySelector('[data-checked="true"]') as HTMLElement
61
+ checkedEl?.scrollIntoView({ behavior: "smooth", block: "center" })
62
+ }
63
+ scrollToChecked(hourWrapperRef.current)
64
+ scrollToChecked(minWrapperRef.current)
65
+ }, [selectedHour, selectedMin])
66
+
67
+ function CalendarRow({ year, month }: { year: number; month: string }) {
68
+ const days = getDaysInMonthWithWeekday(year, month)
69
+
70
+ const mapToThaiWeek = (dayIndex: number) => dayIndex % 7
71
+ const firstDayThaiIndex = mapToThaiWeek(new Date(`${year}-${month}-01`).getDay())
72
+ const emptyStartDays = Array(firstDayThaiIndex).fill(null)
73
+
74
+ const allDays = [...emptyStartDays, ...days.map((d) => d.date.split("-")[2])]
75
+ while (allDays.length < 42) allDays.push(null)
76
+
77
+ const weeks: Array<Array<string | null>> = []
78
+ for (let i = 0; i < allDays.length; i += 7) weeks.push(allDays.slice(i, i + 7))
79
+
80
+ return (
81
+ <>
82
+ {weeks.map((week, rowIndex) => (
83
+ <div key={rowIndex} className="DBui-inputDateTimeRowDay">
84
+ {week.map((day, i) => (
85
+ <p
86
+ key={i}
87
+ className="DBui-inputDateTimeDay"
88
+ onClick={() => {
89
+ if (day) {
90
+ setSelectedDay(`${getYearInDateString(showDay)}-${getMonthInDateString(showDay)}-${day}`)
91
+ }
92
+ }}
93
+ data-checked={
94
+ `${getYearInDateString(selectedDay)}-${getMonthInDateString(selectedDay)}-${
95
+ getDayInDateString(selectedDay).split("T")[0]
96
+ }` === `${getYearInDateString(showDay)}-${getMonthInDateString(showDay)}-${day}`
97
+ }
98
+ data-hidden-hover={!day}
99
+ >
100
+ {day || ""}
101
+ </p>
102
+ ))}
103
+ </div>
104
+ ))}
105
+ </>
106
+ )
107
+ }
108
+
109
+ const readingValue = (dateString: string) => {
110
+ const [date, time] = dateString.split("T")
111
+ return `${date} ${time}`
112
+ }
113
+
114
+ const portalRoot =
115
+ (document.getElementById("root") as HTMLElement) ||
116
+ (document.getElementById("__next") as HTMLElement) ||
117
+ document.body
118
+
119
+ return (
120
+ <Controller
121
+ name={name}
122
+ control={control}
123
+ render={({ field, fieldState }) => {
124
+ const realValue: string = field.value || ""
125
+ const isInvalid = fieldState.invalid
126
+ const errorMessage = fieldState.error?.message
127
+ const setValue = field.onChange
128
+
129
+ // ✅ sync RHF value -> local state (เหมือน useEffect [realValue] ของ Formik)
130
+ // eslint-disable-next-line react-hooks/rules-of-hooks
131
+ useEffect(() => {
132
+ if (!realValue) {
133
+ const now = getDateNow()
134
+ setShowDay(now)
135
+ setSelectedDay(now)
136
+ setSelectedHour("00")
137
+ setSelectedMin("00")
138
+ return
139
+ }
140
+
141
+ const [d, t] = realValue.split("T")
142
+ const [h, m] = t.split(":")
143
+
144
+ setShowDay(d)
145
+ setSelectedDay(d)
146
+ setSelectedHour(h || "00")
147
+ setSelectedMin(m || "00")
148
+ }, [realValue])
149
+
150
+ const handleClearState = (e: React.MouseEvent<HTMLImageElement>) => {
151
+ e.stopPropagation()
152
+ setValue("")
153
+ }
154
+
155
+ return (
156
+ <InputBase
157
+ name={name}
158
+ label={label}
159
+ require={require}
160
+ fullWidth={fullWidth}
161
+ isInvalid={isInvalid}
162
+ errorMessage={errorMessage}
163
+ >
164
+ <>
165
+ <div
166
+ className="DBui-inputDateTime"
167
+ onClick={() => (disabled ? null : setIsOpenModal(true))}
168
+ data-invalid={isInvalid}
169
+ data-disabled={disabled}
170
+ >
171
+ <p>{realValue ? readingValue(realValue) : placeholder}</p>
172
+ <img
173
+ src={closedSVG}
174
+ className="DBui-clearIconInputDateTime"
175
+ onClick={handleClearState}
176
+ data-hidden={realValue === "" || disabled || isHideClearIcon}
177
+ />
178
+ </div>
179
+
180
+ {createPortal(
181
+ <Modal id="modalInputDateTime" open={isOpenModal} onClose={() => setIsOpenModal(false)}>
182
+ <div className="DBui-inputDateTimeWrapperCalendar">
183
+ <div className="DBui-inputDateTimeWrapperYearMonthDayTime">
184
+ <div className="DBui-inputDateTimeWrapperYearMonthDay">
185
+ <div className="DBui-inputDateTimeRowMonthYear">
186
+ <img
187
+ src={expandArrowSVG}
188
+ className="DBui-inputDateTimeRowMonthYearSelected"
189
+ style={{ transform: "rotate(90deg)" }}
190
+ onClick={() => {
191
+ setShowDay(
192
+ updateMonthInDateString(
193
+ showDay,
194
+ String(Number(showDay.split("-")[1]) - 1).padStart(2, "0"),
195
+ ),
196
+ )
197
+ }}
198
+ />
199
+
200
+ <Menu
201
+ isInModal
202
+ trigger={() => (
203
+ <h4 className="DBui-inputDateTimeRowMonthYearSelected">{showDay.slice(0, 4)}</h4>
204
+ )}
205
+ >
206
+ {({ close }) =>
207
+ yearOption.map((data, index) => (
208
+ <p
209
+ key={index}
210
+ className="DBui-inputDateTimeRowMonthYearOption"
211
+ onClick={() => {
212
+ setShowDay(updateYearInDateString(showDay, data.value))
213
+ close()
214
+ }}
215
+ data-checked={getYearInDateString(showDay) === data.value}
216
+ >
217
+ {data.label}
218
+ </p>
219
+ ))
220
+ }
221
+ </Menu>
222
+
223
+ <Menu
224
+ isInModal
225
+ trigger={() => (
226
+ <h4 className="DBui-inputDateTimeRowMonthYearSelected" style={{ width: "7rem" }}>
227
+ {monthOption.find((e) => e.value === showDay.slice(5, 7))?.label}
228
+ </h4>
229
+ )}
230
+ >
231
+ {({ close }) =>
232
+ monthOption.map((data, index) => (
233
+ <p
234
+ key={index}
235
+ className="DBui-inputDateTimeRowMonthYearOption"
236
+ onClick={() => {
237
+ setShowDay(updateMonthInDateString(showDay, data.value))
238
+ close()
239
+ }}
240
+ data-checked={getMonthInDateString(showDay) === data.value}
241
+ >
242
+ {data.label}
243
+ </p>
244
+ ))
245
+ }
246
+ </Menu>
247
+
248
+ <img
249
+ src={expandArrowSVG}
250
+ className="DBui-inputDateTimeRowMonthYearSelected"
251
+ style={{ transform: "rotate(-90deg)" }}
252
+ onClick={() => {
253
+ setShowDay(
254
+ updateMonthInDateString(
255
+ showDay,
256
+ String(Number(showDay.split("-")[1]) + 1).padStart(2, "0"),
257
+ ),
258
+ )
259
+ }}
260
+ />
261
+ </div>
262
+
263
+ <div>
264
+ <div className="DBui-inputDateTimeRowHeaderDay">
265
+ <p className="DBui-inputDateTimeHeader">Sun</p>
266
+ <p className="DBui-inputDateTimeHeader">Mon</p>
267
+ <p className="DBui-inputDateTimeHeader">Tue</p>
268
+ <p className="DBui-inputDateTimeHeader">Wed</p>
269
+ <p className="DBui-inputDateTimeHeader">Thu</p>
270
+ <p className="DBui-inputDateTimeHeader">Fri</p>
271
+ <p className="DBui-inputDateTimeHeader">Sat</p>
272
+ </div>
273
+
274
+ <CalendarRow year={getYearInDateString(showDay)} month={getMonthInDateString(showDay)} />
275
+ </div>
276
+ </div>
277
+
278
+ <div className="DBui-inputDateTimeWrapperHourMin">
279
+ <p style={{ textAlign: "center" }}>Hr</p>
280
+ <div className="DBui-inputDateTimeWrapperHourMinOptions" ref={hourWrapperRef}>
281
+ {hourOption.map((hour) => (
282
+ <p
283
+ key={hour.value}
284
+ data-checked={selectedHour === hour.value}
285
+ onClick={() => setSelectedHour(hour.value)}
286
+ >
287
+ {hour.value}
288
+ </p>
289
+ ))}
290
+ </div>
291
+ </div>
292
+
293
+ <div className="DBui-inputDateTimeWrapperHourMin">
294
+ <p style={{ textAlign: "center" }}>Min</p>
295
+ <div className="DBui-inputDateTimeWrapperHourMinOptions" ref={minWrapperRef}>
296
+ {minOption.map((min) => (
297
+ <p
298
+ key={min.value}
299
+ data-checked={selectedMin === min.value}
300
+ onClick={() => setSelectedMin(min.value)}
301
+ >
302
+ {min.value}
303
+ </p>
304
+ ))}
305
+ </div>
306
+ </div>
307
+ </div>
308
+
309
+ <div className="DBui-inputDateTimeWrapperButton">
310
+ <Button
311
+ type="button"
312
+ onClick={() => {
313
+ let hour = getHourNow()
314
+ let min = getMinNow()
315
+ if (selectedHour) hour = selectedHour
316
+ if (selectedMin) min = selectedMin
317
+
318
+ setValue(`${selectedDay}T${hour}:${min}`)
319
+ setIsOpenModal(false)
320
+ }}
321
+ name="Save"
322
+ className="DBui-inputDateTimeButtonSave"
323
+ />
324
+ <Button
325
+ type="button"
326
+ onClick={() => setIsOpenModal(false)}
327
+ name="Cancle"
328
+ className="DBui-inputDateTimeButtonCancle"
329
+ />
330
+ </div>
331
+ </div>
332
+ </Modal>,
333
+ portalRoot,
334
+ )}
335
+ </>
336
+ </InputBase>
337
+ )
338
+ }}
339
+ />
340
+ )
341
+ }
342
+
343
+ export default InputDateTime
344
+
345
+ function generateHourOptions(): { label: string; value: string }[] {
346
+ return Array.from({ length: 24 }, (_, i) => {
347
+ const value = String(i).padStart(2, "0")
348
+ return { label: value, value }
349
+ })
350
+ }
351
+
352
+ function generateMinuteOptions(): { label: string; value: string }[] {
353
+ return Array.from({ length: 60 }, (_, i) => {
354
+ const value = String(i).padStart(2, "0")
355
+ return { label: value, value }
356
+ })
357
+ }
358
+
359
+ function getHourNow(): string {
360
+ const dateNow = new Date()
361
+ return `${dateNow.getHours()}`.padStart(2, "0")
362
+ }
363
+
364
+ function getMinNow(): string {
365
+ const dateNow = new Date()
366
+ return `${dateNow.getMinutes()}`.padStart(2, "0")
367
+ }
@@ -0,0 +1,11 @@
1
+ export type PropsInputDateTime = {
2
+ name: string
3
+ label?: string
4
+ placeholder?: string
5
+ disabled?: boolean
6
+ require?: boolean
7
+ fullWidth?: boolean
8
+ isHideClearIcon?: boolean
9
+ minYear?: number
10
+ maxYear?: number
11
+ }
@@ -0,0 +1,9 @@
1
+ @import '../extend.scss';
2
+
3
+ .DBui-inputNumber {
4
+ @extend .DBui-input;
5
+
6
+ &:disabled {
7
+ @extend .DBui-disabled;
8
+ }
9
+ }
@@ -0,0 +1,118 @@
1
+ // Lib
2
+ import React, { useEffect, useState } from "react"
3
+ import { useFormContext, Controller } from "react-hook-form"
4
+
5
+ // Include in project
6
+ import "./index.scss"
7
+ import InputBase from "../input-base"
8
+ import type { PropsInputNumber } from "./index.type"
9
+
10
+ const InputNumber: React.FC<PropsInputNumber> = ({
11
+ name,
12
+ label,
13
+ placeholder,
14
+ disabled = false,
15
+ require = false,
16
+ fullWidth = false,
17
+ isPhoneNumber = false,
18
+ isAvailableMinus = false,
19
+ }) => {
20
+ const { control } = useFormContext()
21
+ const [showValue, setShowValue] = useState<string>("")
22
+
23
+ return (
24
+ <Controller
25
+ name={name}
26
+ control={control}
27
+ render={({ field, fieldState }) => {
28
+ const { value, onChange } = field
29
+ const { error, invalid } = fieldState
30
+
31
+ // sync RHF value → showValue
32
+ // eslint-disable-next-line react-hooks/rules-of-hooks
33
+ useEffect(() => {
34
+ if (!isPhoneNumber && typeof value === "number" && !isNaN(value)) {
35
+ setShowValue(value.toLocaleString("en-US"))
36
+ } else if (typeof value === "string") {
37
+ setShowValue(value)
38
+ } else {
39
+ setShowValue(value ?? "")
40
+ }
41
+ }, [value])
42
+
43
+ const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
44
+ const input = event.target.value
45
+
46
+ // ล้างช่อง
47
+ if (input === "") {
48
+ if (isPhoneNumber) {
49
+ onChange("")
50
+ setShowValue("")
51
+ } else {
52
+ onChange(0)
53
+ setShowValue("0")
54
+ }
55
+ return
56
+ }
57
+
58
+ // phone number
59
+ if (isPhoneNumber) {
60
+ onChange(input)
61
+ setShowValue(input)
62
+ return
63
+ }
64
+
65
+ const rawValue = input.replace(/,/g, "")
66
+ const regex = isAvailableMinus ? /^-?[0-9]*\.?[0-9]*$/ : /^[0-9]*\.?[0-9]*$/
67
+
68
+ if (!regex.test(rawValue)) return
69
+
70
+ if (rawValue === "-" || rawValue.endsWith(".")) {
71
+ setShowValue(formatWithComma(rawValue))
72
+ onChange(rawValue)
73
+ return
74
+ }
75
+
76
+ const numericValue = Number(rawValue)
77
+ if (isNaN(numericValue)) return
78
+
79
+ onChange(numericValue)
80
+ setShowValue(formatWithComma(rawValue))
81
+ }
82
+
83
+ return (
84
+ <InputBase
85
+ name={name}
86
+ label={label}
87
+ require={require}
88
+ fullWidth={fullWidth}
89
+ isInvalid={invalid}
90
+ errorMessage={error?.message}
91
+ >
92
+ <input
93
+ className="DBui-inputNumber"
94
+ type="text"
95
+ placeholder={placeholder}
96
+ disabled={disabled}
97
+ value={showValue}
98
+ onChange={handleChange}
99
+ inputMode="decimal"
100
+ pattern="-?[0-9,]*\.?[0-9]*"
101
+ data-invalid={invalid}
102
+ />
103
+ </InputBase>
104
+ )
105
+ }}
106
+ />
107
+ )
108
+ }
109
+
110
+ export default InputNumber
111
+
112
+ const formatWithComma = (val: string): string => {
113
+ if (val === "" || val === "-" || val === "." || val === "-.") return val
114
+
115
+ const [intPart, decPart] = val.split(".")
116
+ const formattedInt = intPart ? Number(intPart).toLocaleString("en-US") : ""
117
+ return decPart !== undefined ? `${formattedInt}.${decPart}` : formattedInt
118
+ }
@@ -0,0 +1,11 @@
1
+ export type PropsInputNumber = {
2
+ name: string
3
+ label?: string
4
+ placeholder?: string
5
+ disabled?: boolean
6
+ require?: boolean
7
+ fullWidth?: boolean
8
+ isPhoneNumber?: boolean // If isPhoneNumber === true type will be string, else type number
9
+ isAvailableMinus?: boolean
10
+ }
11
+
@@ -0,0 +1,22 @@
1
+ @import '../extend.scss';
2
+
3
+ .DBui-wrapInputPassword {
4
+ width: 100%;
5
+ position: relative;
6
+
7
+ .DBui-inputPassword {
8
+ @extend .DBui-input;
9
+
10
+ padding-right: 1em;
11
+ }
12
+
13
+ .DBui-inputPasswordIcon {
14
+ position: absolute;
15
+ right: 5px;
16
+ top: 50%;
17
+ transform: translateY(-50%);
18
+ cursor: pointer;
19
+ width: 1.5em;
20
+ height: 1.5em;
21
+ }
22
+ }
@@ -0,0 +1,60 @@
1
+ // Lib
2
+ import React, { useState } from "react"
3
+ import { useFormContext, useFormState } from "react-hook-form"
4
+
5
+ // Images
6
+ import visibilitySVG from "../../assets/visibility.svg"
7
+ import visibilityOffSVG from "../../assets/visibility-off.svg"
8
+
9
+ // Include in project
10
+ import "./index.scss"
11
+ import InputBase from "../input-base"
12
+ import type { PropsInputPassword } from "./index.type"
13
+
14
+ const InputPassword: React.FC<PropsInputPassword> = ({
15
+ name,
16
+ label,
17
+ placeholder,
18
+ disabled = false,
19
+ require = false,
20
+ fullWidth = false,
21
+ }) => {
22
+ const { register, control } = useFormContext()
23
+ const { errors } = useFormState({ control, name })
24
+
25
+ const error = errors?.[name]
26
+ const isInvalid = Boolean(error)
27
+
28
+ const [isShow, setIsShow] = useState(false)
29
+
30
+ return (
31
+ <InputBase
32
+ name={name}
33
+ label={label}
34
+ require={require}
35
+ fullWidth={fullWidth}
36
+ isInvalid={isInvalid}
37
+ errorMessage={error?.message}
38
+ >
39
+ <div className="DBui-wrapInputPassword">
40
+ <input
41
+ {...register(name)}
42
+ className="DBui-inputPassword"
43
+ type={isShow ? "text" : "password"}
44
+ placeholder={placeholder}
45
+ disabled={disabled}
46
+ data-invalid={isInvalid}
47
+ />
48
+
49
+ <img
50
+ className="DBui-inputPasswordIcon"
51
+ src={isShow ? visibilitySVG : visibilityOffSVG}
52
+ alt="toggle visibility"
53
+ onClick={() => setIsShow((s) => !s)}
54
+ />
55
+ </div>
56
+ </InputBase>
57
+ )
58
+ }
59
+
60
+ export default InputPassword
@@ -0,0 +1,8 @@
1
+ export type PropsInputPassword = {
2
+ name: string
3
+ label?: string
4
+ placeholder?: string
5
+ disabled?: boolean
6
+ require?: boolean
7
+ fullWidth?: boolean
8
+ }
@@ -0,0 +1,35 @@
1
+ @import '../extend.scss';
2
+
3
+ .DBui-wrapInputRadioList {
4
+ display: flex;
5
+ width: 100%;
6
+ gap: 1em;
7
+
8
+ &[data-vertical='true'] {
9
+ flex-direction: column;
10
+ gap: 0.5em;
11
+ }
12
+
13
+ .DBui-wrapInputRadio {
14
+ display: flex;
15
+ align-items: center;
16
+ gap: 0.3em;
17
+
18
+ .DBui-labelRadio {
19
+ &[data-invalid='true'] {
20
+ color: #EB5757;
21
+ }
22
+ }
23
+
24
+ .DBui-inputRadio {
25
+ border-radius: 0.3em;
26
+ width: 20px;
27
+ height: 20px;
28
+ cursor: pointer;
29
+
30
+ &:disabled {
31
+ @extend .DBui-disabled;
32
+ }
33
+ }
34
+ }
35
+ }