@liguelead/design-system 0.0.29 → 0.0.30

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 (31) hide show
  1. package/components/Alert/Alert.stories.tsx +1 -1
  2. package/components/Button/Button.stories.tsx +1 -1
  3. package/components/Checkbox/Checkbox.stories.tsx +2 -2
  4. package/components/Combobox/Combobox.stories.tsx +1 -1
  5. package/components/DatePicker/DatePicker.stories.tsx +85 -20
  6. package/components/DatePicker/DatePicker.styles.ts +394 -50
  7. package/components/DatePicker/DatePicker.tsx +353 -118
  8. package/components/DatePicker/DatePicker.types.ts +40 -24
  9. package/components/Dialog/Dialog.stories.tsx +1 -1
  10. package/components/IconButton/IconButton.stories.tsx +1 -1
  11. package/components/InputOpt/InputOpt.stories.tsx +2 -2
  12. package/components/LinkButton/LinkButton.stories.tsx +4 -2
  13. package/components/PageWrapper/PageWrapper.stories.tsx +1 -1
  14. package/components/RadioButton/RadioButton.stories.tsx +2 -2
  15. package/components/RequiredAsterisk/RequiredAsterisk.stories.tsx +1 -1
  16. package/components/SegmentedButton/SegmentedButton.stories.tsx +1 -1
  17. package/components/Select/Select.stories.tsx +2 -2
  18. package/components/Table/Table.stories.tsx +53 -0
  19. package/components/Table/Table.styles.ts +76 -0
  20. package/components/Table/Table.tsx +157 -0
  21. package/components/Table/Table.types.ts +16 -0
  22. package/components/Table/index.ts +1 -0
  23. package/components/Table/utils/getPageNumbers.ts +35 -0
  24. package/components/Table/utils/index.ts +1 -0
  25. package/components/Table/utils/types.ts +4 -0
  26. package/components/Text/Text.stories.tsx +2 -2
  27. package/components/TextField/TextField.stories.tsx +1 -1
  28. package/components/Toaster/Toaster.stories.tsx +1 -1
  29. package/components/Wizard/Wizard.stories.tsx +2 -2
  30. package/components/index.ts +1 -0
  31. package/package.json +2 -1
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react'
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import Alert from './Alert'
3
3
 
4
4
  const meta: Meta<typeof Alert> = {
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react'
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { themes } from '@liguelead/foundation'
3
3
  import Button from './Button'
4
4
  import type { ButtonVariantTypes } from './Button.types'
@@ -1,11 +1,11 @@
1
- import type { Meta, StoryObj } from '@storybook/react'
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import Checkbox from './Checkbox'
3
3
 
4
4
  const meta: Meta<typeof Checkbox> = {
5
5
  title: 'Form/Checkbox',
6
6
  component: Checkbox,
7
7
  parameters: {
8
- layout: 'padded',
8
+ layout: 'centered',
9
9
  },
10
10
  tags: ['autodocs'],
11
11
  argTypes: {
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react'
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { useState } from 'react'
3
3
  import Combobox from './Combobox'
4
4
  import { iconMap, iconOptions } from '../../../stories/utils/icons'
@@ -1,43 +1,108 @@
1
- import type { Meta, StoryObj } from '@storybook/react'
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import type { DateRange } from 'react-day-picker'
3
+ import { useState } from 'react'
2
4
  import DatePicker from './DatePicker'
3
5
 
4
- const meta: Meta<typeof DatePicker> = {
6
+ const meta = {
5
7
  title: 'Form/DatePicker',
6
8
  component: DatePicker,
7
9
  parameters: {
8
- layout: 'padded',
10
+ layout: 'centered'
9
11
  },
10
12
  tags: ['autodocs'],
11
13
  argTypes: {
12
- label: {
13
- control: 'text',
14
- description: 'DatePicker label'
15
- },
16
14
  size: {
17
15
  control: 'select',
18
16
  options: ['sm', 'md', 'lg'],
19
- description: 'DatePicker size'
17
+ description: 'Size of the DatePicker'
18
+ },
19
+ mode: {
20
+ control: 'select',
21
+ options: ['single', 'range'],
22
+ description: 'Selection mode of the DatePicker'
20
23
  },
21
- startYear: {
22
- control: 'number',
23
- description: 'Start year for year selection'
24
+ label: {
25
+ control: 'text',
26
+ description: 'Label for the DatePicker'
24
27
  },
25
- endYear: {
26
- control: 'number',
27
- description: 'End year for year selection'
28
+ placeholder: {
29
+ control: 'text',
30
+ description: 'Placeholder text when no date is selected'
31
+ },
32
+ disabled: {
33
+ control: 'boolean',
34
+ description: 'Whether the DatePicker is disabled'
28
35
  },
29
- icon: {
36
+ inline: {
30
37
  control: 'boolean',
31
- description: 'Show calendar icon'
38
+ description: 'Whether to display the DatePicker inline'
39
+ },
40
+ onChange: {
41
+ action: 'changed',
42
+ description: 'Callback fired when a date is selected (single mode)'
43
+ },
44
+ onRangeChange: {
45
+ action: 'range changed',
46
+ description: 'Callback fired when a date range is selected (range mode)'
32
47
  }
33
- },
34
- }
48
+ }
49
+ } satisfies Meta<typeof DatePicker>
35
50
 
36
51
  export default meta
37
52
  type Story = StoryObj<typeof meta>
38
53
 
39
- export const Default: Story = {
54
+ export const SingleMode: Story = {
55
+ args: {
56
+ size: 'md',
57
+ mode: 'single',
58
+ label: 'Select a date',
59
+ placeholder: 'DD/MM/YYYY',
60
+ disabled: false,
61
+ inline: false,
62
+ value: undefined,
63
+ onChange: () => {}
64
+ },
65
+ render: (args) => {
66
+ const [value, setValue] = useState<Date | undefined>(args.value ?? undefined)
67
+
68
+ return (
69
+ <DatePicker
70
+ mode='single'
71
+ value={value}
72
+ onChange={(next) => {
73
+ setValue(next)
74
+ args.onChange?.(next)
75
+ }}
76
+ />
77
+ )
78
+ }
79
+ }
80
+
81
+ export const RangeMode: Story = {
40
82
  args: {
41
- label: 'Select Date',
83
+ size: 'md',
84
+ mode: 'range',
85
+ label: 'Select a date range',
86
+ placeholder: 'DD/MM/YYYY - DD/MM/YYYY',
87
+ disabled: false,
88
+ inline: false,
89
+ rangeValue: { from: undefined, to: undefined },
90
+ onRangeChange: () => {}
42
91
  },
92
+ render: (args) => {
93
+ const [rangeValue, setRangeValue] = useState<DateRange | undefined>(
94
+ args.rangeValue ?? { from: undefined, to: undefined }
95
+ )
96
+ return (
97
+ <DatePicker
98
+ mode='range'
99
+ rangeValue={rangeValue}
100
+ onRangeChange={(next) => {
101
+ setRangeValue(next)
102
+ args.onRangeChange?.(next)
103
+ }}
104
+ />
105
+ )
106
+ }
43
107
  }
108
+
@@ -1,66 +1,410 @@
1
- import { parseColor } from '../../utils'
2
1
  import styled from 'styled-components'
3
- import { Content, Item } from '@radix-ui/react-dropdown-menu'
4
- import { spacing } from '@liguelead/foundation'
5
-
6
- export const ArrownDatePicker = styled.button.attrs(() => ({ type: 'button' }))`
7
- background-color: transparent;
8
- border: 0;
9
- outline: 0;
10
- padding: 0;
11
- cursor: pointer;
12
- height: 32px;
13
- width: 32px;
14
- border-radius: 4px;
15
- overflow: hidden;
16
- transition: 0.2s all linear;
17
- color: ${({ theme }) => parseColor(theme.colors.primaryDark)};
18
- &:hover {
19
- color: ${({ theme }) => parseColor(theme.colors.white)};
20
- background: ${({ theme }) => parseColor(theme.colors.primary)};
2
+ import * as Popover from '@radix-ui/react-popover'
3
+ import {
4
+ fontSize,
5
+ fontWeight,
6
+ lineHeight,
7
+ shadow,
8
+ spacing
9
+ } from '@liguelead/foundation'
10
+ import { parseColor } from '../../utils'
11
+ import { DatePickerSize } from './DatePicker.types'
12
+
13
+ const paddingMap: Record<DatePickerSize, number> = {
14
+ sm: Number(spacing.spacing12),
15
+ md: Number(spacing.spacing16),
16
+ lg: Number(spacing.spacing16)
17
+ }
18
+
19
+ const triggerSizes: Record<DatePickerSize, string> = {
20
+ sm: `
21
+ height: 40px;
22
+ font-size: ${fontSize.fontSize14}px;
23
+ `,
24
+ md: `
25
+ height: 44px;
26
+ font-size: ${fontSize.fontSize14}px;
27
+ `,
28
+ lg: `
29
+ height: 48px;
30
+ font-size: ${fontSize.fontSize16}px;
31
+ `
32
+ }
33
+
34
+ const iconOffset = Number(spacing.spacing12) + 18
35
+
36
+ export const Wrapper = styled.div`
37
+ display: flex;
38
+ flex-direction: column;
39
+ gap: ${spacing.spacing4}px;
40
+ width: 100%;
41
+ `
42
+
43
+ export const DatePickerTriggerWrapper = styled.div`
44
+ position: relative;
45
+ width: 100%;
46
+ `
47
+
48
+ export const IconSlot = styled.div<{ $disabled?: boolean }>`
49
+ position: absolute;
50
+ top: 50%;
51
+ left: ${spacing.spacing12}px;
52
+ transform: translateY(-50%);
53
+ display: flex;
54
+ align-items: center;
55
+ pointer-events: none;
56
+ opacity: ${({ $disabled }) => ($disabled ? 0.6 : 1)};
57
+ z-index: 1;
58
+
59
+ &[type="button"] {
60
+ pointer-events: auto;
61
+ cursor: pointer;
62
+ background: transparent;
63
+ border: none;
64
+ padding: 0;
65
+
66
+ &:hover {
67
+ opacity: 0.8;
68
+ }
21
69
  }
22
70
  `
23
71
 
24
- export const ButtonDatePicker = styled.button.attrs(() => ({ type: 'button' }))`
25
- background-color: transparent;
26
- border: 0;
27
- outline: 0;
28
- padding: ${spacing.spacing4}px;
29
- cursor: pointer;
72
+ export const DatePickerHiddenInput = styled.input`
73
+ display: none;
74
+ `
75
+
76
+ const baseTriggerStyles = `
77
+ width: 100%;
30
78
  border-radius: 4px;
31
- overflow: hidden;
32
- transition: 0.2s all linear;
33
- color: ${({ theme }) => parseColor(theme.colors.primaryDark)};
79
+ text-align: left;
80
+ transition: border-color 0.2s ease, box-shadow 0.2s ease, color 0.2s ease;
81
+ `
82
+
83
+ export const DatePickerTrigger = styled.button<{
84
+ $hasIcon?: boolean
85
+ $size: DatePickerSize
86
+ $error?: boolean
87
+ $isPlaceholder?: boolean
88
+ }>`
89
+ ${baseTriggerStyles}
90
+ border: 1px solid
91
+ ${({ theme, $error }) =>
92
+ $error
93
+ ? parseColor(theme.colors.danger200)
94
+ : parseColor(theme.colors.neutral400)};
95
+
96
+ background: ${({ theme }) => parseColor(theme.colors.white)};
97
+
98
+ color: ${({ theme, $isPlaceholder }) =>
99
+ $isPlaceholder
100
+ ? parseColor(theme.colors.textMedium)
101
+ : parseColor(theme.colors.textDark)};
102
+
103
+ ${props => triggerSizes[props.$size]}
104
+
105
+ ${({ $hasIcon, $size }) => {
106
+ const basePadding = paddingMap[$size]
107
+ const leftPadding = $hasIcon
108
+ ? basePadding + iconOffset
109
+ : basePadding
110
+ return `
111
+ padding: 0 ${basePadding}px;
112
+ padding-left: ${leftPadding}px;
113
+ `
114
+ }}
115
+
34
116
  &:hover {
35
- color: ${({ theme }) => parseColor(theme.colors.white)};
36
- background: ${({ theme }) => parseColor(theme.colors.primary)};
117
+ border-color: ${({ theme }) => parseColor(theme.colors.neutral400)};
118
+ background: ${({ theme }) => parseColor(theme.colors.neutral200)};
119
+ color: ${({ theme }) => parseColor(theme.colors.textDark)};
120
+ }
121
+
122
+ &:focus {
123
+ outline: none;
124
+ box-shadow: ${({ theme, $error }) =>
125
+ $error
126
+ ? `0 0 0 3px ${parseColor(theme.colors.danger100)}40`
127
+ : `${shadow.focusShadow}`};
128
+ border-color: ${({ theme, $error }) =>
129
+ $error
130
+ ? parseColor(theme.colors.danger200)
131
+ : parseColor(theme.colors.neutral700)};
37
132
  }
38
133
  `
39
134
 
40
- export const HeaderSelectWrapper = styled.div`
41
- display: flex;
42
- justify-content: space-between;
43
- width: 100%;
44
- gap: 4px;
45
- padding: 0px 4px;
135
+ export const DatePickerInput = styled.input<{
136
+ $hasIcon?: boolean
137
+ $size: DatePickerSize
138
+ $error?: boolean
139
+ }>`
140
+ ${baseTriggerStyles}
141
+ border: 1px solid
142
+ ${({ theme, $error }) =>
143
+ $error
144
+ ? parseColor(theme.colors.danger200)
145
+ : parseColor(theme.colors.neutral400)};
146
+
147
+ background: ${({ theme }) => parseColor(theme.colors.white)};
148
+ color: ${({ theme }) => parseColor(theme.colors.textDark)};
149
+
150
+ ${props => triggerSizes[props.$size]}
151
+
152
+ ${({ $hasIcon, $size }) => {
153
+ const basePadding = paddingMap[$size]
154
+ const leftPadding = $hasIcon
155
+ ? basePadding + iconOffset
156
+ : basePadding
157
+ return `
158
+ padding: 0 ${basePadding}px;
159
+ padding-left: ${leftPadding}px;
160
+ `
161
+ }}
162
+
163
+ &::placeholder {
164
+ color: ${({ theme }) => parseColor(theme.colors.textMedium)};
165
+ }
166
+
167
+ &:hover {
168
+ border-color: ${({ theme }) => parseColor(theme.colors.neutral400)};
169
+ background: ${({ theme }) => parseColor(theme.colors.neutral200)};
170
+ }
171
+
172
+ &:focus {
173
+ outline: none;
174
+ box-shadow: ${({ theme, $error }) =>
175
+ $error
176
+ ? `0 0 0 3px ${parseColor(theme.colors.danger100)}40`
177
+ : `${shadow.focusShadow}`};
178
+ border-color: ${({ theme, $error }) =>
179
+ $error
180
+ ? parseColor(theme.colors.danger200)
181
+ : parseColor(theme.colors.neutral700)};
182
+ }
183
+
184
+ &:disabled {
185
+ opacity: 0.6;
186
+ cursor: not-allowed;
187
+ }
46
188
  `
47
189
 
48
- export const DropdownContent = styled(Content)`
49
- background-color: white;
50
- border-radius: 4px;
51
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
52
- padding: 4px;
53
- max-height: 300px;
54
- overflow-y: auto;
55
- z-index: 10;
190
+ export const DatePickerContent = styled(Popover.Content)`
191
+ background: ${({ theme }) => parseColor(theme.colors.white)};
192
+ border-radius: 8px;
193
+ border: 1px solid ${({ theme }) => parseColor(theme.colors.neutral200)};
194
+ padding: ${spacing.spacing12}px;
195
+
196
+ box-shadow:
197
+ 0 1px 3px 0 rgba(0, 0, 0, 0.10),
198
+ 0 1px 2px -1px rgba(0, 0, 0, 0.10);
199
+ min-width: 240px;
56
200
  `
57
201
 
58
- export const DropdownItem = styled(Item)`
59
- padding: 8px 12px;
60
- font-size: 14px;
61
- cursor: pointer;
62
- &:hover {
63
- background-color: ${({ theme }) => parseColor(theme.colors.primary)};
202
+ export const CalendarWrapper = styled.div<{$singleMonth: boolean}>`
203
+ .rdp-months {
204
+ display: flex;
205
+ gap: ${spacing.spacing24}px;
206
+ }
207
+
208
+ .rdp-caption {
209
+ display: flex;
210
+ justify-content: space-between;
211
+ align-items: center;
212
+ margin-bottom: ${spacing.spacing8}px;
213
+ }
214
+
215
+ .rdp-caption_label {
216
+ font-weight: ${fontWeight.fontWeight500};
217
+ font-size: ${fontSize.fontSize14}px;
218
+ color: ${({ theme }) => parseColor(theme.colors.neutral1100)};
219
+ }
220
+
221
+ .rdp-nav {
222
+ display: flex;
223
+ }
224
+
225
+ .rdp-chevron {
226
+ width: 16px;
227
+ height: 16px;
228
+ fill: ${({ theme }) => parseColor(theme.colors.primary)};
229
+ }
230
+
231
+ .rdp-button_previous, .rdp-button_next {
232
+ height: 32px !important;
233
+ width: 32px;
234
+ border-radius: 4px;
235
+ margin-top: 4px;
236
+ padding: 4px;
237
+ transition: background 0.2s ease;
238
+ }
239
+
240
+ .rdp-button_previous:focus,
241
+ .rdp-button_next:focus {
242
+ box-shadow: ${shadow.focusShadow};
243
+ }
244
+
245
+ .rdp-button_previous:hover,
246
+ .rdp-button_next:hover {
247
+ background: ${({ theme }) => parseColor(theme.colors.primaryDark)};
248
+ & svg {
249
+ fill: ${({ theme }) => parseColor(theme.colors.white)};
250
+ }
251
+ }
252
+
253
+ .rdp-weekday {
254
+ color: ${({ theme }) => parseColor(theme.colors.textMedium)};
255
+ font-size: ${fontSize.fontSize12}px;
256
+ }
257
+
258
+ .rdp-nav_button {
259
+ width: 20px;
260
+ height: 20px;
261
+ border-radius: 4px;
262
+ border: 1px solid ${({ theme }) => parseColor(theme.colors.danger200)};
263
+ color: ${({ theme }) => parseColor(theme.colors.neutral800)};
264
+ transition: background 0.2s ease, border-color 0.2s ease;
265
+ }
266
+
267
+
268
+ .rdp-head_cell {
269
+ font-size: ${fontSize.fontSize12}px;
270
+ color: ${({ theme }) => parseColor(theme.colors.neutral500)};
271
+ text-align: center;
272
+ font-weight: ${fontWeight.fontWeight600};
273
+ }
274
+
275
+ .rdp-day {
276
+ height: 32px;
277
+ width: 32px;
278
+ font-size: ${fontSize.fontSize14}px;
279
+ cursor: pointer;
280
+ transition: 0.2s;
281
+ color: ${({ theme }) => parseColor(theme.colors.neutral800)};
282
+ margin: 0;
283
+ padding: 0;
284
+ }
285
+
286
+ .rdp-range_middle,
287
+ .rdp-range_start,
288
+ .rdp-range_end {
289
+ background: transparent !important;
290
+ }
291
+
292
+ .rdp-range_middle .rdp-day_button {
293
+ background-color: ${({ theme }) => parseColor(theme.colors.primaryLight)} !important;
294
+ padding: 0;
295
+ margin-bottom: 8px;
296
+ }
297
+
298
+ .rdp-day_button {
299
+ margin-bottom: 8px;
300
+ height: 32px;
301
+ width: 32px;
302
+ font-size: ${fontSize.fontSize14}px;
303
+ color: ${({ theme }) => parseColor(theme.colors.textDark)};
304
+ font-weight: ${fontWeight.fontWeight400};
305
+ }
306
+
307
+ .rdp-day_button:hover {
308
+ background: ${({ theme }) => parseColor(theme.colors.primaryDark)};
64
309
  color: ${({ theme }) => parseColor(theme.colors.white)};
310
+ border-radius: 4px;
311
+ }
312
+
313
+ .rdp-range_middle {
314
+ padding: 0;
65
315
  }
316
+
317
+ .rdp-range_end .rdp-day_button,
318
+ .rdp-range_start .rdp-day_button {
319
+ background: ${({ theme }) => parseColor(theme.colors.primary)} !important;
320
+ color: ${({ theme }) => parseColor(theme.colors.white)} !important;
321
+ font-weight: ${fontWeight.fontWeight400};
322
+ border-radius: 4px;
323
+ border: none;
324
+ }
325
+
326
+ .rdp-range_end .rdp-day_button:focus,
327
+ .rdp-range_start .rdp-day_button:focus {
328
+ box-shadow: ${shadow.focusShadow};
329
+ }
330
+
331
+
332
+ ${({ $singleMonth, theme }) => `
333
+ ${$singleMonth ? `
334
+ .rdp-selected .rdp-day_button {
335
+ background: ${parseColor(theme.colors.primary)} !important;
336
+ color: ${parseColor(theme.colors.white)} !important;
337
+ border-radius: 4px;
338
+ border: none;
339
+ }
340
+
341
+ .rdp-selected .rdp-day_button:focus {
342
+ box-shadow: ${shadow.focusShadow};
343
+ }
344
+ ` : ''}
345
+ `}
346
+
347
+ .rdp-day_today,
348
+ .rdp-today .rdp-day_button {
349
+ background-color: ${({ theme }) => parseColor(theme.colors.neutral300)};
350
+ color: ${({ theme }) => parseColor(theme.colors.textDark)};
351
+ border-radius: 4px;
352
+ border: none;
353
+ }
354
+
355
+ .rdp-range_start.rdp-day_today .rdp-day_button,
356
+ .rdp-range_end.rdp-day_today .rdp-day_button {
357
+ background: ${({ theme }) => parseColor(theme.colors.primary)};
358
+ color: ${({ theme }) => parseColor(theme.colors.white)};
359
+ border-radius: 4px;
360
+ border: none;
361
+ }
362
+
363
+ .rdp-day_outside {
364
+ opacity: 0.45;
365
+ }
366
+
367
+ .rdp-dropdown {
368
+ border-radius: 4px;
369
+ padding: ${spacing.spacing8}px;
370
+ border: 1px solid ${({ theme }) =>
371
+ parseColor(theme.colors.neutral300)};
372
+ background: ${({ theme }) => parseColor(theme.colors.white)};
373
+ cursor: pointer;
374
+ font-size: ${fontSize.fontSize14}px;
375
+ }
376
+
377
+ .rdp-dropdown_root {
378
+ padding: ${spacing.spacing8}px;
379
+ border-radius: 4px;
380
+ border: 1px solid ${({ theme }) =>
381
+ parseColor(theme.colors.neutral400)};
382
+ cursor: pointer;
383
+ font-size: ${fontSize.fontSize12}px;
384
+ color: ${({ theme }) => parseColor(theme.colors.textDark)};
385
+ }
386
+
387
+ .rdp-dropdown .rdp-months_dropdown {
388
+ & option {
389
+ padding: ${spacing.spacing20}px;
390
+ }
391
+ }
392
+
393
+ .rdp-dropdown:focus-visible ~ .rdp-caption_label {
394
+ outline: 1px solid white;
395
+ }
396
+ `
397
+
398
+ export const DatePickerLabel = styled.label`
399
+ font-size: ${fontSize.fontSize14}px;
400
+ font-weight: ${fontWeight.fontWeight500};
401
+ margin-bottom: 2px;
402
+ color: ${({ theme }) => parseColor(theme.colors.neutral800)};
403
+ `
404
+
405
+ export const DatePickerError = styled.span`
406
+ font-size: ${fontSize.fontSize12}px;
407
+ line-height: ${lineHeight.lineHeight16}px;
408
+ color: ${({ theme }) => parseColor(theme.colors.danger200)};
409
+ margin-top: 2px;
66
410
  `