kahuna-base-react-components 1.0.6 → 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.
- package/.prettierrc +8 -0
- package/README.md +23 -0
- package/dist/components/KButton/KButton.d.ts +23 -0
- package/dist/components/KButton/index.d.ts +1 -0
- package/dist/components/KCodeInput/KCodeInput.d.ts +36 -0
- package/dist/components/KCodeInput/index.d.ts +1 -0
- package/dist/components/KDropdown/KDropdown.d.ts +38 -0
- package/dist/components/KDropdown/index.d.ts +1 -0
- package/dist/components/KDropdownToggle/KDropdownToggle.d.ts +35 -0
- package/dist/components/KDropdownToggle/index.d.ts +1 -0
- package/dist/components/KInput/KInput.d.ts +32 -0
- package/dist/components/KInput/index.d.ts +1 -0
- package/dist/components/KLogo/KLogo.d.ts +18 -0
- package/dist/components/KLogo/index.d.ts +1 -0
- package/dist/components/KSelectDate/KSelectDate.d.ts +9 -0
- package/dist/components/KSelectDate/index.d.ts +1 -0
- package/dist/components/KSlider/KSlider.d.ts +15 -0
- package/dist/components/KSlider/index.d.ts +1 -0
- package/dist/components/KSliderLabel/KSliderLabel.d.ts +19 -0
- package/dist/components/KSliderLabel/index.d.ts +1 -0
- package/dist/components/KSpan/KSpan.d.ts +18 -0
- package/dist/components/KSpan/index.d.ts +1 -0
- package/dist/components/KTextArea/KTextArea.d.ts +32 -0
- package/dist/components/KTextArea/index.d.ts +1 -0
- package/dist/components/KTitleSpan/KTitleSpan.d.ts +14 -0
- package/dist/components/KTitleSpan/index.d.ts +1 -0
- package/dist/components/KTooltip/KTooltip.d.ts +19 -0
- package/dist/components/KTooltip/index.d.ts +1 -0
- package/dist/fonts/AeonikPro-Bold/AeonikPro-Bold.otf +0 -0
- package/dist/fonts/AeonikPro-Bold/CoType EULA Desktop.pdf +1388 -3
- package/dist/fonts/Inter/Inter-VariableFont_slnt,wght.ttf +0 -0
- package/dist/fonts/Inter/OFL.txt +93 -0
- package/dist/fonts/Inter/README.txt +72 -0
- package/dist/fonts/Inter/static/Inter-Black.ttf +0 -0
- package/dist/fonts/Inter/static/Inter-Bold.ttf +0 -0
- package/dist/fonts/Inter/static/Inter-ExtraBold.ttf +0 -0
- package/dist/fonts/Inter/static/Inter-ExtraLight.ttf +0 -0
- package/dist/fonts/Inter/static/Inter-Light.ttf +0 -0
- package/dist/fonts/Inter/static/Inter-Medium.ttf +0 -0
- package/dist/fonts/Inter/static/Inter-Regular.ttf +0 -0
- package/dist/fonts/Inter/static/Inter-SemiBold.ttf +0 -0
- package/dist/fonts/Inter/static/Inter-Thin.ttf +0 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.esm.js +9 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +241 -8
- package/package.json +14 -3
- package/postcss.config.js +6 -0
- package/removeUseClient.js +22 -0
- package/rollup.config.js +9 -3
- package/src/assets/calendar-hovered.svg +3 -0
- package/src/assets/calendar.svg +3 -0
- package/src/assets/check.svg +3 -0
- package/src/assets/chevron-left.svg +7 -0
- package/src/assets/chevron-right.svg +7 -0
- package/src/assets/fonts/AeonikPro/AeonikPro-Bold.otf +0 -0
- package/src/assets/fonts/AeonikPro/AeonikPro-Medium.otf +0 -0
- package/src/assets/fonts/AeonikPro/CoType EULA Desktop.pdf +1388 -3
- package/src/assets/fonts/Inter/Inter-VariableFont_slnt,wght.ttf +0 -0
- package/src/assets/fonts/Inter/OFL.txt +93 -0
- package/src/assets/fonts/Inter/README.txt +72 -0
- package/src/assets/fonts/Inter/static/Inter-Black.ttf +0 -0
- package/src/assets/fonts/Inter/static/Inter-Bold.ttf +0 -0
- package/src/assets/fonts/Inter/static/Inter-ExtraBold.ttf +0 -0
- package/src/assets/fonts/Inter/static/Inter-ExtraLight.ttf +0 -0
- package/src/assets/fonts/Inter/static/Inter-Light.ttf +0 -0
- package/src/assets/fonts/Inter/static/Inter-Medium.ttf +0 -0
- package/src/assets/fonts/Inter/static/Inter-Regular.ttf +0 -0
- package/src/assets/fonts/Inter/static/Inter-SemiBold.ttf +0 -0
- package/src/assets/fonts/Inter/static/Inter-Thin.ttf +0 -0
- package/src/assets/kahuna-text.svg +3 -0
- package/src/assets/logo-gray.svg +10 -0
- package/src/assets/logo-small-hovered.svg +4 -0
- package/src/assets/logo-small.svg +11 -0
- package/src/assets/logo.svg +11 -0
- package/src/assets/progress.svg +37 -0
- package/src/assets/separator.svg +3 -0
- package/src/assets/slider-dots.svg +7 -0
- package/src/assets/slider-step.svg +3 -0
- package/src/assets/slider-thumb.svg +3 -0
- package/src/assets/tracks.svg +5 -0
- package/src/assets/union.svg +3 -0
- package/src/assets/vector.svg +3 -0
- package/src/components/KButton/KButton.stories.tsx +69 -12
- package/src/components/KButton/KButton.tsx +51 -14
- package/src/components/KCodeInput/KCodeInput.stories.tsx +84 -0
- package/src/components/KCodeInput/KCodeInput.tsx +260 -0
- package/src/components/KCodeInput/index.ts +1 -0
- package/src/components/KDropdown/KDropdown.stories.tsx +90 -0
- package/src/components/KDropdown/KDropdown.tsx +201 -0
- package/src/components/KDropdown/index.ts +1 -0
- package/src/components/KInput/KInput.stories.tsx +84 -0
- package/src/components/KInput/KInput.tsx +128 -0
- package/src/components/KInput/index.ts +1 -0
- package/src/components/KLogo/KLogo.stories.tsx +24 -0
- package/src/components/KLogo/KLogo.tsx +79 -0
- package/src/components/KLogo/index.ts +1 -0
- package/src/components/KSelectDate/CalendarCustom.css +235 -0
- package/src/components/KSelectDate/KSelectDate.stories.tsx +54 -0
- package/src/components/KSelectDate/KSelectDate.tsx +314 -0
- package/src/components/KSelectDate/index.ts +1 -0
- package/src/components/KSlider/KSlider.stories.tsx +19 -0
- package/src/components/KSlider/KSlider.tsx +67 -0
- package/src/components/KSlider/index.ts +1 -0
- package/src/components/KSliderLabel/KSliderLabel.stories.tsx +61 -0
- package/src/components/KSliderLabel/KSliderLabel.tsx +137 -0
- package/src/components/KSliderLabel/index.ts +1 -0
- package/src/components/KSpan/KSpan.stories.tsx +31 -11
- package/src/components/KSpan/KSpan.tsx +65 -9
- package/src/components/KTextArea/KTextArea.stories.tsx +65 -0
- package/src/components/KTextArea/KTextArea.tsx +130 -0
- package/src/components/KTextArea/index.ts +1 -0
- package/src/components/KTitleSpan/KTitleSpan.stories.tsx +23 -0
- package/src/components/KTitleSpan/KTitleSpan.tsx +33 -0
- package/src/components/KTitleSpan/index.ts +1 -0
- package/src/components/KTooltip/KTooltip.stories.tsx +94 -0
- package/src/components/KTooltip/KTooltip.tsx +67 -0
- package/src/components/KTooltip/index.ts +1 -0
- package/src/index.ts +12 -1
- package/src/main.css +251 -0
- package/tailwind.config.js +9 -0
- package/tsconfig.json +6 -1
- package/src/stories/Button.stories.ts +0 -50
- package/src/stories/Button.tsx +0 -48
- package/src/stories/Configure.mdx +0 -364
- package/src/stories/Header.stories.ts +0 -27
- package/src/stories/Header.tsx +0 -56
- package/src/stories/Page.stories.ts +0 -32
- package/src/stories/Page.tsx +0 -73
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +0 -5
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +0 -15
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +0 -3
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +0 -12
- package/src/stories/assets/youtube.svg +0 -4
- package/src/stories/button.css +0 -30
- package/src/stories/header.css +0 -32
- package/src/stories/page.css +0 -69
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import React, { CSSProperties, useEffect, useState } from "react"
|
|
2
|
+
import Calendar from "react-calendar"
|
|
3
|
+
import "./CalendarCustom.css"
|
|
4
|
+
//@ts-ignore
|
|
5
|
+
import LeftIcon from "../../assets/chevron-left.svg"
|
|
6
|
+
//@ts-ignore
|
|
7
|
+
import SeparatorIcon from "../../assets/separator.svg"
|
|
8
|
+
//@ts-ignore
|
|
9
|
+
import CalendarIcon from "../../assets/calendar.svg"
|
|
10
|
+
//@ts-ignore
|
|
11
|
+
import RightIcon from "../../assets/chevron-right.svg"
|
|
12
|
+
import "../../main.css"
|
|
13
|
+
import KButton from "../KButton"
|
|
14
|
+
import KSpan from "../KSpan"
|
|
15
|
+
|
|
16
|
+
export interface KSelectDateProps {
|
|
17
|
+
value: Date | undefined
|
|
18
|
+
onChange: (date: Date | undefined) => void
|
|
19
|
+
}
|
|
20
|
+
interface MonthSelectorType {
|
|
21
|
+
monthName: string
|
|
22
|
+
year: string
|
|
23
|
+
date: Date
|
|
24
|
+
}
|
|
25
|
+
interface DaySelectorType {
|
|
26
|
+
dayName: string
|
|
27
|
+
dayOrderInMonth: number
|
|
28
|
+
date: Date
|
|
29
|
+
}
|
|
30
|
+
interface MonthTextType {
|
|
31
|
+
[key: string]: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const KSelectDate: React.FC<KSelectDateProps> = (props) => {
|
|
35
|
+
const [value, setValue] = useState<Date | undefined>(props.value)
|
|
36
|
+
const [calendarDate, setCalendarDate] = useState<Date | undefined>(props.value)
|
|
37
|
+
const [dummyDate, setDummyDate] = useState<Date | undefined>(props.value)
|
|
38
|
+
const [nextMonths, setNextMonths] = useState<MonthSelectorType[]>([])
|
|
39
|
+
const [weekDays, setWeekDays] = useState<DaySelectorType[]>([])
|
|
40
|
+
const [openCalendar, setOpenCalendar] = useState<boolean>(false)
|
|
41
|
+
|
|
42
|
+
const formatShortWeekday = (locale: string | undefined, date: Date): string => {
|
|
43
|
+
return date.toLocaleDateString(locale, { weekday: "short" }).charAt(0) // Return only the first letter of the weekday
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const formatMonthYear = (locale: string | undefined, date: Date): string => {
|
|
47
|
+
const formattedDate = date.toLocaleDateString(locale, { month: "short", year: "numeric" })
|
|
48
|
+
const [month, year] = formattedDate.split(" ")
|
|
49
|
+
const capitalizedMonth = month.charAt(0).toUpperCase() + month.slice(1).toLowerCase()
|
|
50
|
+
return `${capitalizedMonth}, ${year}`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const onClickDay = (date: Date) => {
|
|
54
|
+
if (date.getTime() === calendarDate?.getTime()) {
|
|
55
|
+
setCalendarDate(undefined)
|
|
56
|
+
} else {
|
|
57
|
+
setCalendarDate(date)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const getNextMonths = (date: Date | undefined) => {
|
|
62
|
+
if (date) {
|
|
63
|
+
const updatedMonths = Array.from({ length: 4 }, (_, i) => {
|
|
64
|
+
const newDate = new Date(date.getFullYear(), date.getMonth() + (i - 1), 1)
|
|
65
|
+
return {
|
|
66
|
+
monthName: newDate.toLocaleString("en-US", { month: "long" }),
|
|
67
|
+
year: newDate.getFullYear().toString(),
|
|
68
|
+
date: newDate
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
setNextMonths(updatedMonths)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const getWeekDays = (date: Date | undefined) => {
|
|
76
|
+
if (date) {
|
|
77
|
+
const startOfWeek = new Date(date)
|
|
78
|
+
const dayOfWeek = startOfWeek.getDay()
|
|
79
|
+
const diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek // If Sunday (0), move back 6 days; otherwise move back (1 - dayOfWeek) days
|
|
80
|
+
startOfWeek.setDate(startOfWeek.getDate() + diffToMonday)
|
|
81
|
+
|
|
82
|
+
const updatedDays = Array.from({ length: 7 }, (_, i) => {
|
|
83
|
+
const day = new Date(startOfWeek)
|
|
84
|
+
day.setDate(startOfWeek.getDate() + i)
|
|
85
|
+
return {
|
|
86
|
+
dayName: day.toLocaleDateString("en-US", { weekday: "short" }),
|
|
87
|
+
dayOrderInMonth: day.getDate(),
|
|
88
|
+
date: day
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
setWeekDays(updatedDays)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const renderPopUpCalendar = () => {
|
|
97
|
+
return (
|
|
98
|
+
<div className="flex flex-col gap-0">
|
|
99
|
+
<Calendar
|
|
100
|
+
onClickDay={onClickDay}
|
|
101
|
+
locale="en-US"
|
|
102
|
+
value={calendarDate || null}
|
|
103
|
+
defaultValue={null}
|
|
104
|
+
next2Label={null}
|
|
105
|
+
prev2Label={null}
|
|
106
|
+
prevLabel={<img src={LeftIcon} />}
|
|
107
|
+
nextLabel={<img src={RightIcon} />}
|
|
108
|
+
formatShortWeekday={formatShortWeekday}
|
|
109
|
+
formatMonthYear={formatMonthYear}
|
|
110
|
+
/>
|
|
111
|
+
<div className="h-19 w-[350px] bg-[#FFF] flex flex-row gap-4 py-4 justify-center border-[1px] border-[#E7E7E7] border-t-0 rounded-b-[10px]">
|
|
112
|
+
<KButton
|
|
113
|
+
text="Cancel"
|
|
114
|
+
height="44px"
|
|
115
|
+
width="160px"
|
|
116
|
+
background="#FFF"
|
|
117
|
+
textColor="#111"
|
|
118
|
+
onClick={() => {
|
|
119
|
+
setOpenCalendar(false)
|
|
120
|
+
}}
|
|
121
|
+
/>
|
|
122
|
+
<KButton
|
|
123
|
+
text="Apply"
|
|
124
|
+
height="44px"
|
|
125
|
+
width="160px"
|
|
126
|
+
background="#F2FE67"
|
|
127
|
+
textColor="#111"
|
|
128
|
+
onClick={() => {
|
|
129
|
+
setValue(calendarDate)
|
|
130
|
+
setOpenCalendar(false)
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const monthSelector = (month: string, year: string, date: Date) => {
|
|
139
|
+
const monthText: MonthTextType = {
|
|
140
|
+
January: "Jan",
|
|
141
|
+
February: "Feb",
|
|
142
|
+
March: "March",
|
|
143
|
+
April: "April",
|
|
144
|
+
May: "May",
|
|
145
|
+
June: "June",
|
|
146
|
+
July: "July",
|
|
147
|
+
August: "August",
|
|
148
|
+
September: "Sept",
|
|
149
|
+
October: "Oct",
|
|
150
|
+
November: "Nov",
|
|
151
|
+
December: "Dec"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const text = `${monthText[month]}, ${year}`
|
|
155
|
+
|
|
156
|
+
const inMonth = dummyDate ? dummyDate?.getMonth() === date.getMonth() : new Date().getMonth() === date.getMonth()
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div
|
|
160
|
+
key={`${text}-${date}`}
|
|
161
|
+
className={`w-[135px] h-9 box-sizing`}
|
|
162
|
+
style={{
|
|
163
|
+
borderRadius: 999,
|
|
164
|
+
border: inMonth ? "1px solid #111" : "1px solid #E7E7E7"
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
<KButton
|
|
168
|
+
text={text}
|
|
169
|
+
shadowDisabled={true}
|
|
170
|
+
onClick={() => {
|
|
171
|
+
if (date.getTime()) {
|
|
172
|
+
setDummyDate(date)
|
|
173
|
+
}
|
|
174
|
+
}}
|
|
175
|
+
background={inMonth ? "#111" : "#FFF"}
|
|
176
|
+
textColor={inMonth? "#FFF" : "#111"}
|
|
177
|
+
borderRadius={999}
|
|
178
|
+
padding="8px 16px"
|
|
179
|
+
height="34px"
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const daySelector = (text: string, date: Date) => {
|
|
186
|
+
return (
|
|
187
|
+
<div
|
|
188
|
+
key={`${text}-${date}`}
|
|
189
|
+
className={`w-[85px] h-[104px] flex flex-col justify-between py-3 px-2.5 rounded-[10px] ${
|
|
190
|
+
date.getTime() === value?.getTime() ? "bg-[#F8FEA3]" : "bg-[#F5F5F5]"
|
|
191
|
+
} cursor-pointer`}
|
|
192
|
+
onClick={() => {
|
|
193
|
+
if (date.getTime() === value?.getTime()) {
|
|
194
|
+
setValue(undefined)
|
|
195
|
+
} else {
|
|
196
|
+
setValue(date)
|
|
197
|
+
}
|
|
198
|
+
}}
|
|
199
|
+
>
|
|
200
|
+
<div>
|
|
201
|
+
<img src={CalendarIcon} alt="calendar" />
|
|
202
|
+
</div>
|
|
203
|
+
<KSpan text={text} fontWeight={500} color="#111" />
|
|
204
|
+
</div>
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const changeWeeks = (date: Date | undefined, isNextWeek: boolean) => {
|
|
209
|
+
if (date) {
|
|
210
|
+
const newDate = new Date(date)
|
|
211
|
+
if (isNextWeek) {
|
|
212
|
+
newDate.setDate(newDate.getDate() + 7)
|
|
213
|
+
} else {
|
|
214
|
+
newDate.setDate(newDate.getDate() - 7)
|
|
215
|
+
}
|
|
216
|
+
setDummyDate(newDate)
|
|
217
|
+
} else {
|
|
218
|
+
const newDate = new Date()
|
|
219
|
+
if (isNextWeek) {
|
|
220
|
+
newDate.setDate(newDate.getDate() + 7)
|
|
221
|
+
} else {
|
|
222
|
+
newDate.setDate(newDate.getDate() - 7)
|
|
223
|
+
}
|
|
224
|
+
setDummyDate(newDate)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (value) {
|
|
230
|
+
setDummyDate(value)
|
|
231
|
+
}
|
|
232
|
+
props.onChange(value)
|
|
233
|
+
}, [value])
|
|
234
|
+
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
const today = new Date()
|
|
237
|
+
if (!props.value) {
|
|
238
|
+
getNextMonths(today)
|
|
239
|
+
getWeekDays(today)
|
|
240
|
+
}
|
|
241
|
+
}, [])
|
|
242
|
+
|
|
243
|
+
useEffect(() => {
|
|
244
|
+
getNextMonths(dummyDate)
|
|
245
|
+
getWeekDays(dummyDate)
|
|
246
|
+
}, [dummyDate])
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<React.Fragment>
|
|
250
|
+
{openCalendar && (
|
|
251
|
+
<div className="w-[100vw] h-[100vh] fixed left-0 top-0 flex items-center justify-center z-50">
|
|
252
|
+
<div>{renderPopUpCalendar()}</div>
|
|
253
|
+
</div>
|
|
254
|
+
)}
|
|
255
|
+
<div>
|
|
256
|
+
<div className={`flex flex-col gap-4 ${openCalendar && "blur-2xl"}`}>
|
|
257
|
+
<div className="flex flex-row justify-between gap-2 items-center">
|
|
258
|
+
<div className="flex flex-row gap-2">
|
|
259
|
+
{nextMonths.map((month, i) => {
|
|
260
|
+
return monthSelector(month.monthName, month.year, month.date)
|
|
261
|
+
})}
|
|
262
|
+
</div>
|
|
263
|
+
<div>
|
|
264
|
+
<img src={SeparatorIcon} />
|
|
265
|
+
</div>
|
|
266
|
+
<div>
|
|
267
|
+
<KButton
|
|
268
|
+
icon={CalendarIcon}
|
|
269
|
+
onClick={() => {
|
|
270
|
+
setOpenCalendar(true)
|
|
271
|
+
setCalendarDate(value)
|
|
272
|
+
}}
|
|
273
|
+
padding="8px"
|
|
274
|
+
height="36px"
|
|
275
|
+
background="#FFF"
|
|
276
|
+
/>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
<div className="flex flex-row justify-between gap-1 items-center">
|
|
280
|
+
{weekDays.map((day, i) => {
|
|
281
|
+
return daySelector(`${day.dayOrderInMonth}, ${day.dayName}`, day.date)
|
|
282
|
+
})}
|
|
283
|
+
</div>
|
|
284
|
+
<div className="flex flex-row justify-between items-center">
|
|
285
|
+
<KButton
|
|
286
|
+
text="Previous Week"
|
|
287
|
+
padding="6px"
|
|
288
|
+
leftIcon={LeftIcon}
|
|
289
|
+
onClick={() => {
|
|
290
|
+
changeWeeks(dummyDate, false)
|
|
291
|
+
}}
|
|
292
|
+
width="130px"
|
|
293
|
+
height="32px"
|
|
294
|
+
background="#FFF"
|
|
295
|
+
/>
|
|
296
|
+
<KButton
|
|
297
|
+
text="Next Week"
|
|
298
|
+
padding="6px"
|
|
299
|
+
rightIcon={RightIcon}
|
|
300
|
+
onClick={() => {
|
|
301
|
+
changeWeeks(dummyDate, true)
|
|
302
|
+
}}
|
|
303
|
+
width="130px"
|
|
304
|
+
height="32px"
|
|
305
|
+
background="#FFF"
|
|
306
|
+
/>
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
</React.Fragment>
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export default KSelectDate
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './KSelectDate';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {Meta, StoryFn} from "@storybook/react";
|
|
2
|
+
import KSlider, {SliderOption} from "./KSlider";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "ReactComponentLibrary/KSlider",
|
|
6
|
+
component: KSlider,
|
|
7
|
+
} as Meta<typeof KSlider>;
|
|
8
|
+
|
|
9
|
+
const Template: StoryFn<typeof KSlider> = (args) => <KSlider {...args} />;
|
|
10
|
+
|
|
11
|
+
export const KSliderPrimary = Template.bind({});
|
|
12
|
+
KSliderPrimary.args = {
|
|
13
|
+
options: [{label: "0%", value: 0}, {label: "25%", value: 1}, {label: "50%", value: 2}, {label: "75%", value: 3}],
|
|
14
|
+
onChange: (option: SliderOption) => {
|
|
15
|
+
// Do Nothing
|
|
16
|
+
},
|
|
17
|
+
width: "200px",
|
|
18
|
+
value: 3
|
|
19
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import "../../main.css"
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import StepIcon from "../../assets/slider-step.svg"
|
|
5
|
+
|
|
6
|
+
export interface SliderOption {
|
|
7
|
+
label: string
|
|
8
|
+
value: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface KSliderProps {
|
|
12
|
+
options: SliderOption[]
|
|
13
|
+
onChange: (option: SliderOption) => void
|
|
14
|
+
value?: number
|
|
15
|
+
disabled?: boolean
|
|
16
|
+
width?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const KSlider: React.FC<KSliderProps> = (props) => {
|
|
20
|
+
const disabled = props.disabled || false
|
|
21
|
+
const width = props.width || "100%"
|
|
22
|
+
const height = "8px"
|
|
23
|
+
|
|
24
|
+
const calculateSpanWidth = ():number => {
|
|
25
|
+
const min = props.options[0].value
|
|
26
|
+
const max = props.options[props.options.length - 1].value
|
|
27
|
+
const spanWidth = (props.value! - min) / (max - min) * 100
|
|
28
|
+
if (spanWidth >= 100 ) {
|
|
29
|
+
return 99
|
|
30
|
+
}
|
|
31
|
+
return spanWidth
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="inline-block bg-[#E7E7E7] rounded-full relative z-0" style={{ width, height }}>
|
|
36
|
+
<span className="w-full flex justify-between absolute top-0 z-0 items-center px-0.5" style={{ height }}>
|
|
37
|
+
{props.options.map((option, index) => {
|
|
38
|
+
return <img key={`step-icon-${index}`} src={StepIcon} className="w-1 h-1 !z-0 " />
|
|
39
|
+
})}
|
|
40
|
+
</span>
|
|
41
|
+
<span
|
|
42
|
+
className="block z-50 absolute top-0"
|
|
43
|
+
style={{
|
|
44
|
+
height,
|
|
45
|
+
width: `${calculateSpanWidth()}%`,
|
|
46
|
+
borderRadius: "10px",
|
|
47
|
+
backgroundColor: "black"
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
<input
|
|
51
|
+
disabled={disabled}
|
|
52
|
+
style={{ width, height }}
|
|
53
|
+
className={"k-slider-input absolute top-0 !z-50"}
|
|
54
|
+
onChange={(e) => {
|
|
55
|
+
const option = props.options.find((option) => option.value.toString() === e.target.value)
|
|
56
|
+
if (option) return props.onChange(option)
|
|
57
|
+
}}
|
|
58
|
+
value={props.value}
|
|
59
|
+
type="range"
|
|
60
|
+
min={props.options[0].value}
|
|
61
|
+
max={props.options[props.options.length - 1].value}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default KSlider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './KSlider';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Meta, StoryFn } from "@storybook/react";
|
|
3
|
+
import KSliderLabel, { SliderLabelProps, SliderLabelOption } from "./KSliderLabel";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: "ReactComponentLibrary/KSliderLabel",
|
|
7
|
+
component: KSliderLabel,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "centered",
|
|
10
|
+
},
|
|
11
|
+
} as Meta<typeof KSliderLabel>;
|
|
12
|
+
|
|
13
|
+
const KSliderLabelWrapper: React.FC<SliderLabelProps> = (args) => {
|
|
14
|
+
const [selectedValue, setSelectedValue] = useState<number | undefined>(0);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
console.log("selectedValue: ", selectedValue);
|
|
18
|
+
}, [selectedValue]);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<KSliderLabel
|
|
22
|
+
{...args}
|
|
23
|
+
value={selectedValue}
|
|
24
|
+
onChange={(option) => {
|
|
25
|
+
console.log("option: ", option);
|
|
26
|
+
setSelectedValue(option.value);
|
|
27
|
+
console.log("Value updated to: ", option.value);
|
|
28
|
+
}}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const Template: StoryFn<typeof KSliderLabelWrapper> = (args) => <KSliderLabelWrapper {...args} />;
|
|
34
|
+
|
|
35
|
+
const options: SliderLabelOption[] = [
|
|
36
|
+
{ label: "Low", value: 0 },
|
|
37
|
+
{ label: "Medium", value: 1 },
|
|
38
|
+
{ label: "Medium", value: 3 },
|
|
39
|
+
{ label: "Medium", value: 4 },
|
|
40
|
+
{ label: "Medium", value: 5 },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export const KSliderLabelPrimary = Template.bind({});
|
|
44
|
+
KSliderLabelPrimary.args = {
|
|
45
|
+
options,
|
|
46
|
+
disabled: false,
|
|
47
|
+
width: "440px",
|
|
48
|
+
titleText: "Slider Title",
|
|
49
|
+
valueText: "50",
|
|
50
|
+
fontSize: 14,
|
|
51
|
+
color: "#000",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const KSliderLabelHoverText = Template.bind({});
|
|
55
|
+
KSliderLabelHoverText.args = {
|
|
56
|
+
options,
|
|
57
|
+
titleText: "Hover to see me!",
|
|
58
|
+
valueText: "50",
|
|
59
|
+
fontSize: 14,
|
|
60
|
+
color: "#000",
|
|
61
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react"
|
|
2
|
+
import "../../main.css"
|
|
3
|
+
//@ts-ignore
|
|
4
|
+
import ThumbIcon from "../../assets/slider-thumb.svg"
|
|
5
|
+
import KSpan from "../KSpan"
|
|
6
|
+
|
|
7
|
+
export interface SliderLabelOption {
|
|
8
|
+
label: string
|
|
9
|
+
value: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SliderLabelProps {
|
|
13
|
+
options: SliderLabelOption[]
|
|
14
|
+
onChange: (option: SliderLabelOption) => void
|
|
15
|
+
value?: number
|
|
16
|
+
disabled?: boolean
|
|
17
|
+
width?: string
|
|
18
|
+
titleText?: string
|
|
19
|
+
valueText?: string,
|
|
20
|
+
fontSize?: number,
|
|
21
|
+
color?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const KSliderLabel: React.FC<SliderLabelProps> = (props) => {
|
|
25
|
+
const disabled = props.disabled || false
|
|
26
|
+
const width = props.width || "100%"
|
|
27
|
+
const height = "48px"
|
|
28
|
+
const fontSize = props.fontSize || 14
|
|
29
|
+
const color = props.color || "#000"
|
|
30
|
+
|
|
31
|
+
const [titleFits, setTitleFits] = useState<boolean>(false)
|
|
32
|
+
const [valueFits, setValueFits] = useState<boolean>(false)
|
|
33
|
+
|
|
34
|
+
const titleTextRef = useRef<HTMLDivElement>(null)
|
|
35
|
+
const valueTextRef = useRef<HTMLDivElement>(null)
|
|
36
|
+
const progressRef = useRef<HTMLDivElement>(null)
|
|
37
|
+
const mainDivRef = useRef<HTMLDivElement>(null)
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
|
|
41
|
+
if (!titleTextRef.current || !progressRef.current || !valueTextRef.current || !mainDivRef.current) return
|
|
42
|
+
|
|
43
|
+
const titleWidth = titleTextRef.current.getBoundingClientRect().width
|
|
44
|
+
const progressWidth = progressRef.current.getBoundingClientRect().width
|
|
45
|
+
|
|
46
|
+
setTitleFits(titleWidth < progressWidth - 30)
|
|
47
|
+
|
|
48
|
+
const valueWidth = valueTextRef.current.getBoundingClientRect().width
|
|
49
|
+
const restWidth = mainDivRef.current.getBoundingClientRect().width - progressWidth
|
|
50
|
+
|
|
51
|
+
setValueFits(valueWidth < restWidth)
|
|
52
|
+
|
|
53
|
+
}, [props.value])
|
|
54
|
+
|
|
55
|
+
const calculateSpanWidth = (): string => {
|
|
56
|
+
if (!props.value) return "32px"
|
|
57
|
+
const min = props.options[0].value
|
|
58
|
+
const max = props.options[props.options.length - 1].value
|
|
59
|
+
const spanWidth = `calc((((${props.value! - min}) / (${max - min})) * (100% - 32px)) + 32px)`
|
|
60
|
+
return spanWidth
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="flex flex-col gap-1 w-full">
|
|
65
|
+
<div className={"flex flex-row items-center justify-between z-[200]"}>
|
|
66
|
+
<div
|
|
67
|
+
ref={titleTextRef}
|
|
68
|
+
className="relative pl-4"
|
|
69
|
+
style={{
|
|
70
|
+
top: titleFits ? "40px" : "0px",
|
|
71
|
+
transitionDuration: "0.3s"
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
<KSpan
|
|
75
|
+
text={props.titleText || ""}
|
|
76
|
+
color={color}
|
|
77
|
+
fontSize={fontSize}
|
|
78
|
+
lineHeight="20px"
|
|
79
|
+
letterSpacing="-0.084px"
|
|
80
|
+
fontWeight={500}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
<div
|
|
84
|
+
ref={valueTextRef}
|
|
85
|
+
className="relative pr-4"
|
|
86
|
+
style={{
|
|
87
|
+
top: valueFits ? "40px" : "0px",
|
|
88
|
+
transitionDuration: "0.3s"
|
|
89
|
+
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
<KSpan
|
|
93
|
+
text={props.valueText || ""}
|
|
94
|
+
color={color}
|
|
95
|
+
fontSize={fontSize}
|
|
96
|
+
lineHeight="20px"
|
|
97
|
+
letterSpacing="-0.084px"
|
|
98
|
+
fontWeight={500}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div ref={mainDivRef} className="inline-block bg-[#f7f7f7] rounded-[8px] relative z-0" style={{ width, height }}>
|
|
104
|
+
<span className="w-full flex justify-between absolute top-0 z-0 items-center" style={{ height }} />
|
|
105
|
+
<span
|
|
106
|
+
ref={progressRef}
|
|
107
|
+
className="block items-center z-50 top-0 relative"
|
|
108
|
+
style={{
|
|
109
|
+
height,
|
|
110
|
+
width: calculateSpanWidth(),
|
|
111
|
+
borderRadius: "8px",
|
|
112
|
+
backgroundColor: "rgba(102, 102, 102, 0.05)"
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
<span className="flex justify-center h-4 w-4 absolute right-2 top-4 z-[1000]">
|
|
116
|
+
<img src={ThumbIcon} alt="thumb" className="" />
|
|
117
|
+
</span>
|
|
118
|
+
</span>
|
|
119
|
+
<input
|
|
120
|
+
disabled={disabled}
|
|
121
|
+
style={{ width, height }}
|
|
122
|
+
className={"k-slider-label-input absolute top-0 !z-50"}
|
|
123
|
+
onChange={(e) => {
|
|
124
|
+
const option = props.options.find((option) => option.value.toString() === e.target.value)
|
|
125
|
+
if (option) return props.onChange(option)
|
|
126
|
+
}}
|
|
127
|
+
value={props.value}
|
|
128
|
+
type="range"
|
|
129
|
+
min={props.options[0].value}
|
|
130
|
+
max={props.options[props.options.length - 1].value}
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default KSliderLabel
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './KSliderLabel';
|
|
@@ -1,17 +1,37 @@
|
|
|
1
|
-
import {Meta, StoryFn} from "@storybook/react"
|
|
2
|
-
import
|
|
3
|
-
import KSpan from "./KSpan";
|
|
1
|
+
import { Meta, StoryFn } from "@storybook/react"
|
|
2
|
+
import KSpan from "./KSpan"
|
|
4
3
|
|
|
5
4
|
export default {
|
|
6
5
|
title: "ReactComponentLibrary/KSpan",
|
|
7
6
|
component: KSpan,
|
|
8
|
-
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: "centered"
|
|
9
|
+
}
|
|
10
|
+
} as Meta<typeof KSpan>
|
|
9
11
|
|
|
10
|
-
const Template: StoryFn<typeof
|
|
12
|
+
const Template: StoryFn<typeof KSpan> = (args) => <KSpan {...args} />
|
|
11
13
|
|
|
12
|
-
export const
|
|
13
|
-
|
|
14
|
-
text: "
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
export const KSpanPrimary = Template.bind({})
|
|
15
|
+
KSpanPrimary.args = {
|
|
16
|
+
text: "Hello World",
|
|
17
|
+
fontSize: 14,
|
|
18
|
+
color: "#737373",
|
|
19
|
+
fontWeight: 400,
|
|
20
|
+
lineHeight: "20px",
|
|
21
|
+
fontStyle: "normal",
|
|
22
|
+
letterSpacing: "-0.084px",
|
|
23
|
+
textDecoration: "underline"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
export const KSpanHoverText = Template.bind({})
|
|
28
|
+
KSpanHoverText.args = {
|
|
29
|
+
text: "Hello World",
|
|
30
|
+
fontSize: 14,
|
|
31
|
+
fontWeight: 400,
|
|
32
|
+
lineHeight: "20px",
|
|
33
|
+
color: "#111",
|
|
34
|
+
fontStyle: "normal",
|
|
35
|
+
letterSpacing: "-0.084px",
|
|
36
|
+
hoverText: "Hover"
|
|
37
|
+
}
|