@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,284 @@
1
+ // Lib
2
+ import React, { useEffect, 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 { PropsInputDateRange } from "./index.type"
13
+ import Menu from "../../menu"
14
+ import Modal from "../../modal"
15
+ import { createPortal } from "react-dom"
16
+ import {
17
+ generateMonthOptions,
18
+ generateYearOptions,
19
+ getDateNow,
20
+ getDaysInMonthWithWeekday,
21
+ getMonthInDateString,
22
+ getYearInDateString,
23
+ updateMonthInDateString,
24
+ updateYearInDateString,
25
+ } from "../input-date"
26
+
27
+ const InputDateRange: React.FC<PropsInputDateRange> = ({
28
+ name,
29
+ label,
30
+ placeholder,
31
+ disabled = false,
32
+ require = false,
33
+ fullWidth = false,
34
+ isHideClearIcon = true,
35
+ maxYear,
36
+ minYear,
37
+ }) => {
38
+ const { control } = useFormContext()
39
+
40
+ const [showDay, setShowDay] = useState<string>(getDateNow())
41
+ const [isOpenModal, setIsOpenModal] = useState(false)
42
+
43
+ const yearOption = generateYearOptions(minYear, maxYear)
44
+ const monthOption = generateMonthOptions()
45
+
46
+ const [mounted, setMounted] = useState(false)
47
+ useEffect(() => {
48
+ setMounted(true)
49
+ return () => setMounted(false)
50
+ }, [])
51
+
52
+ const portalRoot =
53
+ (document.getElementById("root") as HTMLElement) ||
54
+ (document.getElementById("__next") as HTMLElement) ||
55
+ document.body
56
+
57
+ return (
58
+ <Controller
59
+ name={name}
60
+ control={control}
61
+ render={({ field, fieldState }) => {
62
+ const value: [string, string] = field.value ?? ["", ""]
63
+ const [startDate, endDate] = value
64
+ const { onChange } = field
65
+ const { invalid, error } = fieldState
66
+
67
+ // sync เดือนที่โชว์ตอนเปิด modal
68
+ // eslint-disable-next-line react-hooks/rules-of-hooks
69
+ useEffect(() => {
70
+ if (isOpenModal) {
71
+ setShowDay(startDate || getDateNow())
72
+ }
73
+ }, [isOpenModal])
74
+
75
+ function CalendarRow({ year, month }: { year: number; month: string }) {
76
+ const days = getDaysInMonthWithWeekday(year, month)
77
+
78
+ const mapToThaiWeek = (dayIndex: number) => dayIndex % 7
79
+ const firstDayThaiIndex = mapToThaiWeek(new Date(`${year}-${month}-01`).getDay())
80
+ const emptyStartDays = Array(firstDayThaiIndex).fill(null)
81
+
82
+ const allDays = [...emptyStartDays, ...days.map((d) => d.date.split("-")[2])]
83
+ while (allDays.length < 42) allDays.push(null)
84
+
85
+ const weeks: Array<Array<string | null>> = []
86
+ for (let i = 0; i < allDays.length; i += 7) weeks.push(allDays.slice(i, i + 7))
87
+
88
+ return (
89
+ <>
90
+ {weeks.map((week, rowIndex) => (
91
+ <div key={rowIndex} className="DBui-inputDateRangeRowDay">
92
+ {week.map((day, i) => {
93
+ if (!day) {
94
+ return (
95
+ <p key={i} className="DBui-inputDateRangeDay" data-hidden-hover>
96
+ {" "}
97
+ </p>
98
+ )
99
+ }
100
+
101
+ const targetDate = `${getYearInDateString(showDay)}-${getMonthInDateString(showDay)}-${day}`
102
+
103
+ const isChecked =
104
+ startDate && !endDate
105
+ ? targetDate === startDate
106
+ : startDate && endDate
107
+ ? targetDate >= startDate && targetDate <= endDate
108
+ : false
109
+
110
+ return (
111
+ <p
112
+ key={i}
113
+ className="DBui-inputDateRangeDay"
114
+ data-checked={isChecked}
115
+ onClick={() => {
116
+ if (!startDate) {
117
+ onChange([targetDate, ""])
118
+ } else if (startDate && !endDate) {
119
+ if (targetDate >= startDate) {
120
+ onChange([startDate, targetDate])
121
+ setIsOpenModal(false)
122
+ } else {
123
+ onChange([targetDate, ""])
124
+ }
125
+ } else {
126
+ onChange([targetDate, ""])
127
+ }
128
+ }}
129
+ >
130
+ {day}
131
+ </p>
132
+ )
133
+ })}
134
+ </div>
135
+ ))}
136
+ </>
137
+ )
138
+ }
139
+
140
+ const handleClear = (e: React.MouseEvent<HTMLImageElement>) => {
141
+ e.stopPropagation()
142
+ onChange(["", ""])
143
+ }
144
+
145
+ return (
146
+ <InputBase
147
+ name={name}
148
+ label={label}
149
+ require={require}
150
+ fullWidth={fullWidth}
151
+ isInvalid={invalid}
152
+ errorMessage={error?.message}
153
+ >
154
+ <>
155
+ <div
156
+ className="DBui-inputDateRange"
157
+ onClick={() => (disabled ? null : setIsOpenModal(true))}
158
+ data-invalid={invalid}
159
+ data-disabled={disabled}
160
+ >
161
+ <p>{startDate ? `${startDate}${endDate ? ` to ${endDate}` : ""}` : placeholder}</p>
162
+
163
+ <img
164
+ src={closedSVG}
165
+ className="DBui-clearIconInputDateRange"
166
+ onClick={handleClear}
167
+ data-hidden={(startDate === "" && endDate === "") || disabled || isHideClearIcon}
168
+ />
169
+ </div>
170
+
171
+ {mounted &&
172
+ createPortal(
173
+ <Modal id="modalInputDateRange" open={isOpenModal} onClose={() => setIsOpenModal(false)}>
174
+ <div className="DBui-inputDateRangeWrapperCalendar">
175
+ <div className="DBui-inputDateRangeRowMonthYear">
176
+ <img
177
+ src={expandArrowSVG}
178
+ className="DBui-inputDateRangeRowMonthYearSelected"
179
+ style={{ transform: "rotate(90deg)" }}
180
+ onClick={() => {
181
+ setShowDay(
182
+ updateMonthInDateString(
183
+ showDay,
184
+ String(Number(showDay.split("-")[1]) - 1).padStart(2, "0"),
185
+ ),
186
+ )
187
+ }}
188
+ />
189
+
190
+ <Menu
191
+ isInModal
192
+ trigger={() => (
193
+ <h4 className="DBui-inputDateRangeRowMonthYearSelected">{showDay.slice(0, 4)}</h4>
194
+ )}
195
+ >
196
+ {({ close }) =>
197
+ yearOption.map((y, index) => (
198
+ <p
199
+ key={index}
200
+ className="DBui-inputDateRangeRowMonthYearOption"
201
+ onClick={() => {
202
+ setShowDay(updateYearInDateString(showDay, y.value))
203
+ close()
204
+ }}
205
+ data-checked={getYearInDateString(showDay) === y.value}
206
+ >
207
+ {y.label}
208
+ </p>
209
+ ))
210
+ }
211
+ </Menu>
212
+
213
+ <Menu
214
+ isInModal
215
+ trigger={() => (
216
+ <h4 className="DBui-inputDateRangeRowMonthYearSelected" style={{ width: "7rem" }}>
217
+ {monthOption.find((m) => m.value === showDay.slice(5, 7))?.label}
218
+ </h4>
219
+ )}
220
+ >
221
+ {({ close }) =>
222
+ monthOption.map((m, index) => (
223
+ <p
224
+ key={index}
225
+ className="DBui-inputDateRangeRowMonthYearOption"
226
+ onClick={() => {
227
+ setShowDay(updateMonthInDateString(showDay, m.value))
228
+ close()
229
+ }}
230
+ data-checked={getMonthInDateString(showDay) === m.value}
231
+ >
232
+ {m.label}
233
+ </p>
234
+ ))
235
+ }
236
+ </Menu>
237
+
238
+ <img
239
+ src={expandArrowSVG}
240
+ className="DBui-inputDateRangeRowMonthYearSelected"
241
+ style={{ transform: "rotate(-90deg)" }}
242
+ onClick={() => {
243
+ setShowDay(
244
+ updateMonthInDateString(
245
+ showDay,
246
+ String(Number(showDay.split("-")[1]) + 1).padStart(2, "0"),
247
+ ),
248
+ )
249
+ }}
250
+ />
251
+ </div>
252
+
253
+ <div>
254
+ <div className="DBui-inputDateRangeRowHeaderDay">
255
+ <p className="DBui-inputDateRangeHeader">Sun</p>
256
+ <p className="DBui-inputDateRangeHeader">Mon</p>
257
+ <p className="DBui-inputDateRangeHeader">Tue</p>
258
+ <p className="DBui-inputDateRangeHeader">Wed</p>
259
+ <p className="DBui-inputDateRangeHeader">Thu</p>
260
+ <p className="DBui-inputDateRangeHeader">Fri</p>
261
+ <p className="DBui-inputDateRangeHeader">Sat</p>
262
+ </div>
263
+
264
+ <CalendarRow year={getYearInDateString(showDay)} month={getMonthInDateString(showDay)} />
265
+ </div>
266
+ </div>
267
+ </Modal>,
268
+ portalRoot,
269
+ )}
270
+
271
+ {!mounted && (
272
+ <Modal id="modalInputDateRange" open={isOpenModal} onClose={() => setIsOpenModal(false)}>
273
+ <div className="DBui-inputDateRangeWrapperCalendar" />
274
+ </Modal>
275
+ )}
276
+ </>
277
+ </InputBase>
278
+ )
279
+ }}
280
+ />
281
+ )
282
+ }
283
+
284
+ export default InputDateRange
@@ -0,0 +1,11 @@
1
+ export type PropsInputDateRange = {
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,179 @@
1
+ @import '../extend.scss';
2
+
3
+ .DBui-inputDateTime {
4
+ @extend .DBui-input;
5
+ height: 35px;
6
+ display: flex;
7
+ align-items: center;
8
+ cursor: pointer;
9
+
10
+ &[data-disabled='true'] {
11
+ @extend .DBui-disabled;
12
+ }
13
+
14
+ .DBui-clearIconInputDateTime {
15
+ width: 20px;
16
+
17
+ &[data-hidden='true'] {
18
+ display: none !important;
19
+ }
20
+
21
+ &:hover {
22
+ opacity: 0.8;
23
+ }
24
+ }
25
+ }
26
+
27
+ .DBui-inputDateTimeWrapperCalendar {
28
+ display: flex;
29
+ flex-direction: column;
30
+ padding: 3em 1em;
31
+ gap: 0.8em;
32
+
33
+ @media (max-width: 425px) {
34
+ padding: 2.5em 0;
35
+ width: 99vw;
36
+ }
37
+
38
+ .DBui-inputDateTimeWrapperYearMonthDayTime {
39
+ display: flex;
40
+ flex-direction: row;
41
+ gap: 0.3em;
42
+ justify-content: center;
43
+ align-items: center;
44
+
45
+ @media (max-width: 425px) {
46
+ gap: 0.2em;
47
+ }
48
+
49
+ .DBui-inputDateTimeWrapperYearMonthDay {
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: 0.3em;
53
+ justify-content: center;
54
+ align-items: center;
55
+ border-right: 1px solid gray;
56
+ padding-right: 0.5em;
57
+
58
+
59
+ .DBui-inputDateTimeRowMonthYear,
60
+ .DBui-inputDateTimeRowHeaderDay,
61
+ .DBui-inputDateTimeRowDay {
62
+ display: flex;
63
+ flex-direction: row;
64
+ gap: 0.8em;
65
+
66
+ @media (max-width: 425px) {
67
+ gap: 0.2em;
68
+ }
69
+ }
70
+
71
+ .DBui-inputDateTimeRowMonthYear {
72
+ gap: 0.5em;
73
+ margin-left: 0.5em;
74
+ align-items: center;
75
+ }
76
+
77
+ .DBui-inputDateTimeRowHeaderDay,
78
+ .DBui-inputDateTimeRowDay {
79
+
80
+ .DBui-inputDateTimeHeader,
81
+ .DBui-inputDateTimeDay {
82
+ width: 30px;
83
+ height: 30px;
84
+ text-align: center;
85
+ display: flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ }
89
+
90
+ .DBui-inputDateTimeDay {
91
+
92
+ cursor: not-allowed;
93
+
94
+ &[data-hidden-hover='false'] {
95
+ cursor: pointer;
96
+
97
+ &:hover {
98
+ background-color: #03287E;
99
+ border-radius: 100%;
100
+ color: #FFFFFF;
101
+ }
102
+ }
103
+
104
+ &[data-checked='true'] {
105
+ background-color: #03287E;
106
+ color: #FFFFFF;
107
+ border-radius: 100%;
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ .DBui-inputDateTimeWrapperHourMin {
114
+ padding: 0 0.3em;
115
+ align-self: flex-end;
116
+ flex-direction: column;
117
+ display: flex;
118
+
119
+ @media (max-width: 425px) {
120
+ padding: 0 0.2em;
121
+ }
122
+
123
+ .DBui-inputDateTimeWrapperHourMinOptions {
124
+ max-height: 200px;
125
+ overflow-y: scroll;
126
+ -ms-overflow-style: none;
127
+ /* IE and Edge */
128
+ scrollbar-width: none;
129
+ /* Firefox */
130
+
131
+ /* Hide scrollbar for Chrome, Safari and Opera */
132
+ &::-webkit-scrollbar {
133
+ display: none;
134
+ }
135
+
136
+ p {
137
+ padding: 0.5em;
138
+ cursor: pointer;
139
+
140
+ &:hover {
141
+ background-color: #03287E;
142
+ color: #FFFFFF;
143
+ }
144
+
145
+ &[data-checked='true'] {
146
+ background-color: #03287E;
147
+ color: #FFFFFF;
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ .DBui-inputDateTimeWrapperButton {
155
+ display: flex;
156
+ gap: 0.5em;
157
+ justify-content: flex-end;
158
+ }
159
+ }
160
+
161
+ .DBui-inputDateTimeRowMonthYearSelected {
162
+ cursor: pointer;
163
+ text-align: center;
164
+ }
165
+
166
+ .DBui-inputDateTimeRowMonthYearOption {
167
+ cursor: pointer;
168
+ padding: 0.5em 1em;
169
+
170
+ &:hover {
171
+ background-color: #03287E;
172
+ color: #FFFFFF;
173
+ }
174
+
175
+ &[data-checked='true'] {
176
+ background-color: #03287E;
177
+ color: #FFFFFF;
178
+ }
179
+ }