kahuna-base-react-components 0.2.10 → 0.2.12

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.
@@ -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';
@@ -12,7 +12,8 @@ export interface KSpanProps {
12
12
  hoverText?: string
13
13
  hoverTextColor?: string
14
14
  hoverStyle?: CSSProperties,
15
- textDecoration?: string
15
+ textDecoration?: string,
16
+ ellipsis?: boolean
16
17
  }
17
18
 
18
19
  const KSpan: React.FC<KSpanProps> = (props) => {
@@ -27,9 +28,12 @@ const KSpan: React.FC<KSpanProps> = (props) => {
27
28
  const hoverTextColor = props.hoverTextColor || "#737373"
28
29
  const hoverStyle = props.hoverStyle || {}
29
30
  const textDecoration = props.textDecoration || "none"
31
+ const ellipsis = props.ellipsis || false
32
+
33
+ const ellipsisStyle = {overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap"}
30
34
 
31
35
  const renderBaseSpan = () => {
32
- const baseSpanStyle = { fontSize, color, fontWeight, lineHeight, fontStyle, letterSpacing, textDecoration }
36
+ const baseSpanStyle = { fontSize, color, fontWeight, lineHeight, fontStyle, letterSpacing, textDecoration, ...(ellipsis && ellipsisStyle) }
33
37
  return (
34
38
  <span
35
39
  className={"k-span"}
package/src/index.ts CHANGED
@@ -5,8 +5,8 @@ import KLogo from "./components/KLogo"
5
5
  import KInput from "./components/KInput"
6
6
  import KDropdown from "./components/KDropdown"
7
7
  import KSlider from "./components/KSlider"
8
- import KDropdownToggle from "./components/KDropdownToggle"
8
+ import KSelectDate from "./components/KSelectDate"
9
9
 
10
10
  export {
11
- KButton, KSpan, KLogo, KTitleSpan, KInput, KDropdown, KSlider, KDropdownToggle
11
+ KButton, KSpan, KLogo, KTitleSpan, KInput, KDropdown, KSlider, KSelectDate
12
12
  }
@@ -1,70 +0,0 @@
1
- import { Meta, StoryFn } from "@storybook/react"
2
- import KDropdownToggle, { KSelectOption } from "./KDropdownToggle"
3
- // @ts-ignore
4
- import TracksIcon from "../../assets/tracks.svg"
5
- import { MultiValue } from "react-select"
6
- import { useEffect, useState } from "react"
7
-
8
- export default {
9
- title: "ReactComponentLibrary/KDropdownToggle",
10
- component: KDropdownToggle,
11
- parameters: {
12
- layout: "centered"
13
- }
14
- } as Meta<typeof KDropdownToggle>
15
-
16
- const Template: StoryFn<typeof KDropdownToggle> = (args) => <KDropdownToggle {...args} />
17
-
18
- export const KDropdownToggleSingle = Template.bind({})
19
- KDropdownToggleSingle.args = {
20
- onSelect: (value: KSelectOption | MultiValue<KSelectOption> | null) => {},
21
- width: 250,
22
- placeholder: "Select single...",
23
- options: [
24
- { label: "Label 1", value: 1, icon: TracksIcon },
25
- { label: "Label 2", value: 2, icon: TracksIcon },
26
- { label: "Label 3", value: 3 },
27
- { label: "TANIK", value: 4 }
28
- ]
29
- }
30
-
31
- export const KDropdownToggleMulti = Template.bind({})
32
- KDropdownToggleMulti.args = {
33
- onSelect: (value: KSelectOption | MultiValue<KSelectOption>) => {},
34
- width: 250,
35
- placeholder: "Multi...",
36
- options: [
37
- { label: "Label 1", value: 1, icon: TracksIcon },
38
- { label: "Label 2", value: 2, icon: TracksIcon },
39
- { label: "Label 3", value: 3 }
40
- ],
41
- isMulti: true
42
- }
43
-
44
- export const KDropdownToggleLeftIcon = Template.bind({})
45
- KDropdownToggleLeftIcon.args = {
46
- onSelect: (value: KSelectOption | MultiValue<KSelectOption>) => {},
47
- placeholder: "Placeholder...",
48
- leftIcon: TracksIcon
49
- }
50
-
51
- export const KDropdownToggleRightIcon = Template.bind({})
52
- KDropdownToggleRightIcon.args = {
53
- onSelect: (value: KSelectOption | MultiValue<KSelectOption>) => {},
54
- placeholder: "Placeholder...",
55
- rightIcon: TracksIcon,
56
- width: 250,
57
- options: [
58
- { label: "Label 1", value: 1, icon: TracksIcon },
59
- { label: "Label 2", value: 2, icon: TracksIcon },
60
- { label: "Label 3", value: 3 }
61
- ]
62
- }
63
-
64
- export const KDropdownToggleLeftRightIcon = Template.bind({})
65
- KDropdownToggleLeftRightIcon.args = {
66
- onSelect: (value: KSelectOption | MultiValue<KSelectOption>) => {},
67
- placeholder: "Placeholder...",
68
- leftIcon: TracksIcon,
69
- rightIcon: TracksIcon
70
- }
@@ -1,186 +0,0 @@
1
- import React, { useEffect, useState } from "react"
2
- import "../../main.css"
3
- import Select, { MultiValue } from "react-select"
4
- // @ts-ignore
5
- import CheckIcon from "../../assets/check.svg"
6
- import KSpan from "../KSpan"
7
- import { FilterOptionOption } from "react-select/dist/declarations/src/filters"
8
-
9
- export interface KSelectOption {
10
- label: string
11
- value: number
12
- type?: string
13
- label2?: string
14
- value2?: string
15
- icon?: string
16
- }
17
-
18
- export interface KDropdownProps {
19
- defaultValue?: KSelectOption | MultiValue<KSelectOption>
20
- selected?: KSelectOption | MultiValue<KSelectOption>
21
- onSelect: (selected: KSelectOption | MultiValue<KSelectOption>) => void
22
- options: KSelectOption[]
23
- width?: number
24
- height?: number
25
- leftIcon?: string
26
- rightIcon?: string
27
- background?: string
28
- activeBackground?: string
29
- borderRadius?: number
30
- placeholder?: string
31
- isMulti?: boolean
32
- label?: string
33
- textColor?: string
34
- shadowDisabled?: boolean
35
- menuBackground?: string
36
- padding?: string
37
- gap?: string
38
- hideChosenOptionIcon?: boolean
39
- }
40
-
41
- const KDropdownToggle: React.FC<KDropdownProps> = (props) => {
42
- const [selectedOption, setSelectedOption] = useState<KSelectOption | MultiValue<KSelectOption>>()
43
- const [background, setBackground] = useState("#F5F5F5")
44
-
45
- useEffect(() => {
46
- const emptyBackground = props.background || "#F5F5F5"
47
- const activeBackground = props.activeBackground || "#FFF"
48
-
49
- const background = props.selected ? activeBackground : emptyBackground
50
- setBackground(background)
51
- }, [props.selected])
52
-
53
- const width = props.width || "100%"
54
- const height = props.height || "auto"
55
- const borderRadius = props.borderRadius || 10
56
- const isMulti = props.isMulti || false
57
- const textColor = props.textColor || "#111"
58
- const boxShadow = props.shadowDisabled ? "" : "0 0 0 1px rgba(17, 17, 17, 0.04), 0 1px 1px 0 rgba(17, 17, 17, 0.04)"
59
- const menuBackground = props.menuBackground || "rgb(249, 249, 249)"
60
- const padding = props.padding || "8px"
61
- const gap = props.gap || "4px"
62
- const hideIcon = props.hideChosenOptionIcon || false
63
-
64
- const customFilterOption = (option: FilterOptionOption<KSelectOption>, inputValue: string) => {
65
- return option.data.label.toLocaleLowerCase("en").includes(inputValue.toLocaleLowerCase("en"))
66
- }
67
-
68
- const getOptionLabels = (option: KSelectOption) => {
69
- return (
70
- <div className="flex justify-between" style={{ width: "100%" }}>
71
- <div className="flex">
72
- {option.icon && <img src={option.icon} className="mr-2" width={20} alt={"option-icon"} />}
73
- <KSpan text={option.label} color="#111" />
74
- </div>
75
- {
76
- //@ts-ignore
77
- !isMulti && selectedOption?.value === option.value && <img src={CheckIcon} width={24} alt={"check-icon"} />
78
- }
79
- </div>
80
- )
81
- }
82
- return (
83
- <div
84
- className={"k-dropdown-container"}
85
- style={{ background, borderRadius, width, height, boxShadow, padding, gap }}
86
- >
87
- {props.leftIcon && <img src={props.leftIcon} width={20} alt={"l-icon"} />}
88
-
89
- <Select
90
- defaultValue={props.defaultValue}
91
- isMulti={isMulti}
92
- name={props.label || ""}
93
- placeholder={props.placeholder || ""}
94
- options={props.options}
95
- className={"k-dropdown"}
96
- filterOption={customFilterOption}
97
- isClearable
98
- styles={{
99
- control: (baseStyles, state) => ({
100
- ...baseStyles,
101
- background: "transparent !important",
102
- padding: "0px !important",
103
- boxShadow: "none",
104
- fontSize: 14,
105
- lineHeight: "20px",
106
- cursor: "pointer",
107
- minHeight: "20px",
108
- border: "none"
109
- }),
110
- menu: (base) => ({
111
- ...base,
112
- borderRadius: 10,
113
- background: menuBackground,
114
- boxShadow:
115
- "0px 3px 3px 0px rgba(17, 17, 17, 0.03), 0px 1px 1px 0px rgba(17, 17, 17, 0.04), 0px 0px 0px 1px rgba(17, 17, 17, 0.04)",
116
- backdropFilter: "blur(2px)",
117
- paddingRight: 3,
118
- paddingLeft: 3
119
- }),
120
- menuList: (base) => ({
121
- ...base,
122
- paddingTop: 0,
123
- paddingBottom: 0
124
- }),
125
- singleValue: (provided) => ({
126
- ...provided,
127
- color: textColor
128
- }),
129
- option: (provided, state) => ({
130
- ...provided,
131
- display: "flex",
132
- alignItems: "center",
133
- background: "white",
134
- marginBottom: 4,
135
- marginTop: 4,
136
- borderRadius: 10,
137
- color: "#111"
138
- }),
139
- valueContainer: (base) => ({
140
- ...base,
141
- padding: 0
142
- }),
143
- input: (base) => ({
144
- ...base,
145
- padding: 0,
146
- margin: 0,
147
- height: "20px"
148
- }),
149
- placeholder: (base) => ({
150
- ...base,
151
- margin: 0
152
- }),
153
- clearIndicator: (base) => ({
154
- ...base,
155
- padding: 0
156
- })
157
- }}
158
- components={{
159
- IndicatorSeparator: () => null,
160
- DropdownIndicator: () => null,
161
- SingleValue: ({ data, ...props }) => (
162
- <div className="flex" style={{ position: "absolute" }}>
163
- {data.icon && !hideIcon && <img src={data.icon} className="mr-2" width={20} alt={"data-icon"} />}
164
- <KSpan text={data.label} color="#111" />
165
- </div>
166
- )
167
- }}
168
- onChange={(event) => {
169
- if (!event) {
170
- setSelectedOption(undefined)
171
- return
172
- } else {
173
- setSelectedOption(event)
174
- }
175
- props.onSelect(event)
176
- }}
177
- //@ts-ignore
178
- getOptionLabel={(option: KSelectOption) => getOptionLabels(option)}
179
- />
180
-
181
- {props.rightIcon && <img src={props.rightIcon} width={20} alt={"r-icon"} />}
182
- </div>
183
- )
184
- }
185
-
186
- export default KDropdownToggle
@@ -1 +0,0 @@
1
- export {default} from './KDropdownToggle';