@liguelead/design-system 0.0.28 → 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 (37) hide show
  1. package/components/Alert/Alert.stories.tsx +60 -0
  2. package/components/Button/Button.appearance.ts +2 -2
  3. package/components/Button/Button.stories.tsx +75 -0
  4. package/components/Checkbox/Checkbox.stories.tsx +42 -0
  5. package/components/Combobox/Combobox.stories.tsx +112 -0
  6. package/components/Combobox/Combobox.styles.ts +7 -3
  7. package/components/Combobox/Combobox.tsx +3 -10
  8. package/components/DatePicker/DatePicker.stories.tsx +108 -0
  9. package/components/DatePicker/DatePicker.styles.ts +394 -50
  10. package/components/DatePicker/DatePicker.tsx +353 -118
  11. package/components/DatePicker/DatePicker.types.ts +40 -24
  12. package/components/Dialog/Dialog.stories.tsx +63 -0
  13. package/components/IconButton/IconButton.stories.tsx +49 -0
  14. package/components/InputOpt/InputOpt.stories.tsx +61 -0
  15. package/components/LinkButton/LinkButton.stories.tsx +88 -0
  16. package/components/PageWrapper/PageWrapper.stories.tsx +153 -0
  17. package/components/RadioButton/RadioButton.stories.tsx +70 -0
  18. package/components/RequiredAsterisk/RequiredAsterisk.stories.tsx +44 -0
  19. package/components/SegmentedButton/SegmentedButton.stories.tsx +182 -0
  20. package/components/Select/Select.stories.tsx +63 -0
  21. package/components/Table/Table.stories.tsx +53 -0
  22. package/components/Table/Table.styles.ts +76 -0
  23. package/components/Table/Table.tsx +157 -0
  24. package/components/Table/Table.types.ts +16 -0
  25. package/components/Table/index.ts +1 -0
  26. package/components/Table/utils/getPageNumbers.ts +35 -0
  27. package/components/Table/utils/index.ts +1 -0
  28. package/components/Table/utils/types.ts +4 -0
  29. package/components/Text/Text.stories.tsx +61 -0
  30. package/components/Text/Text.types.ts +1 -0
  31. package/components/TextField/TextField.stories.tsx +84 -0
  32. package/components/Toaster/Toaster.stories.tsx +129 -0
  33. package/components/Toaster/Toaster.ts +19 -0
  34. package/components/Toaster/ToasterProvider.tsx +9 -1
  35. package/components/Wizard/Wizard.stories.tsx +186 -0
  36. package/components/index.ts +1 -0
  37. package/package.json +16 -3
@@ -0,0 +1,61 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import InputOpt from './InputOpt'
3
+
4
+ const meta: Meta<typeof InputOpt> = {
5
+ title: 'Form/InputOpt',
6
+ component: InputOpt,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ argTypes: {
12
+ length: {
13
+ control: 'number',
14
+ description: 'Number of input fields'
15
+ },
16
+ label: {
17
+ control: 'text',
18
+ description: 'Input label'
19
+ },
20
+ helperText: {
21
+ control: 'text',
22
+ description: 'Helper text below input'
23
+ },
24
+ placeholderChar: {
25
+ control: 'text',
26
+ description: 'Placeholder character for empty fields'
27
+ },
28
+ inputMode: {
29
+ control: 'select',
30
+ options: ['numeric', 'text'],
31
+ description: 'Input mode for mobile keyboards'
32
+ },
33
+ autoFocus: {
34
+ control: 'boolean',
35
+ description: 'Auto focus first input on mount'
36
+ },
37
+ disabled: {
38
+ control: 'boolean',
39
+ description: 'Whether inputs are disabled'
40
+ },
41
+ requiredSymbol: {
42
+ control: 'boolean',
43
+ description: 'Show required asterisk'
44
+ },
45
+ error: {
46
+ control: 'boolean',
47
+ description: 'Whether inputs have error state'
48
+ }
49
+ },
50
+ }
51
+
52
+ export default meta
53
+ type Story = StoryObj<typeof meta>
54
+
55
+ export const Default: Story = {
56
+ args: {
57
+ label: 'Verification Code',
58
+ length: 6,
59
+ },
60
+ }
61
+
@@ -0,0 +1,88 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import LinkButton from './LinkButton'
3
+ import { themes } from '@liguelead/foundation'
4
+ import { iconMap, iconOptions } from '../../../stories/utils/icons'
5
+
6
+ type ColorTypes = NonNullable<React.ComponentProps<typeof LinkButton>['color']>
7
+
8
+ const themeColors = Object.keys(themes.spa.colors) as ColorTypes[]
9
+
10
+ const meta: Meta<typeof LinkButton> = {
11
+ title: 'Navigation/LinkButton',
12
+ component: LinkButton,
13
+ parameters: {
14
+ layout: 'centered',
15
+ },
16
+ tags: ['autodocs'],
17
+ argTypes: {
18
+ href: {
19
+ control: 'text',
20
+ description: 'Link URL'
21
+ },
22
+ children: {
23
+ control: 'text',
24
+ description: 'Link content'
25
+ },
26
+ size: {
27
+ control: 'select',
28
+ options: ['sm', 'md', 'lg'],
29
+ description: 'Size of the link button'
30
+ },
31
+ color: {
32
+ control: 'select',
33
+ options: themeColors,
34
+ description: 'Color theme of the link button'
35
+ },
36
+ disabled: {
37
+ control: 'boolean',
38
+ description: 'Whether the link button is disabled'
39
+ },
40
+ fullWidth: {
41
+ control: 'boolean',
42
+ description: 'Whether the link button takes full width'
43
+ },
44
+ leftIcon: {
45
+ control: 'select',
46
+ options: iconOptions,
47
+ description: 'Left icon',
48
+ },
49
+ rightIcon: {
50
+ control: 'select',
51
+ options: iconOptions,
52
+ description: 'Right icon',
53
+ },
54
+ },
55
+ }
56
+
57
+ export default meta
58
+ type Story = StoryObj<typeof meta>
59
+ export const Default: Story = {
60
+ args: {
61
+ href: '#',
62
+ children: 'Link Button',
63
+ size: 'md',
64
+ color: themeColors[0],
65
+ disabled: false,
66
+ fullWidth: false,
67
+ leftIcon: 'none',
68
+ rightIcon: 'none',
69
+ },
70
+ render: (args) => {
71
+ const leftIcon = args.leftIcon === 'none' ? undefined : iconMap[args.leftIcon as keyof typeof iconMap]
72
+ const rightIcon = args.rightIcon === 'none' ? undefined : iconMap[args.rightIcon as keyof typeof iconMap]
73
+
74
+ return (
75
+ <LinkButton
76
+ href={args.href}
77
+ leftIcon={leftIcon}
78
+ rightIcon={rightIcon}
79
+ size={args.size}
80
+ color={args.color}
81
+ disabled={args.disabled}
82
+ fullWidth={args.fullWidth}
83
+ >
84
+ {args.children}
85
+ </LinkButton>
86
+ )
87
+ },
88
+ }
@@ -0,0 +1,153 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import PageWrapper from './PageWrapper'
3
+ import Text from '../Text/Text'
4
+ import Button from '../Button/Button'
5
+
6
+ const meta: Meta<typeof PageWrapper> = {
7
+ title: 'Layout/PageWrapper',
8
+ component: PageWrapper,
9
+ parameters: {
10
+ layout: 'fullscreen',
11
+ },
12
+ tags: ['autodocs'],
13
+ argTypes: {
14
+ width: {
15
+ control: 'text',
16
+ description: 'Maximum width of the wrapper (in pixels)'
17
+ },
18
+ bgColor: {
19
+ control: 'select',
20
+ options: ['primary', 'secondary', 'success', 'warning', 'error', 'white', 'neutral100', 'neutral200'],
21
+ description: 'Background color'
22
+ },
23
+ padding: {
24
+ control: 'select',
25
+ options: ['spacing8', 'spacing12', 'spacing16', 'spacing20', 'spacing24', 'spacing32'],
26
+ description: 'Internal padding'
27
+ }
28
+ },
29
+ }
30
+
31
+ export default meta
32
+ type Story = StoryObj<typeof meta>
33
+
34
+ export const Default: Story = {
35
+ args: {
36
+ children: (
37
+ <div>
38
+ <Text size="heading02">Page Content</Text>
39
+ <Text style={{ marginTop: '16px' }}>
40
+ This is the content inside the page wrapper. It provides consistent spacing and layout.
41
+ </Text>
42
+ </div>
43
+ ),
44
+ },
45
+ }
46
+
47
+ export const WithMaxWidth: Story = {
48
+ args: {
49
+ width: '800',
50
+ children: (
51
+ <div>
52
+ <Text size="heading02">Constrained Width</Text>
53
+ <Text style={{ marginTop: '16px' }}>
54
+ This wrapper has a maximum width of 800px and will be centered on larger screens.
55
+ </Text>
56
+ </div>
57
+ ),
58
+ },
59
+ }
60
+
61
+ export const DifferentBackgrounds: Story = {
62
+ render: () => (
63
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }}>
64
+ <PageWrapper bgColor="white">
65
+ <Text size="heading03">White Background</Text>
66
+ <Text>Default white background wrapper.</Text>
67
+ </PageWrapper>
68
+
69
+ <PageWrapper bgColor="neutral100">
70
+ <Text size="heading03">Neutral Background</Text>
71
+ <Text>Light neutral background wrapper.</Text>
72
+ </PageWrapper>
73
+
74
+ <PageWrapper bgColor="primary" style={{ color: 'white' }}>
75
+ <Text size="heading03" style={{ color: 'white' }}>Primary Background</Text>
76
+ <Text style={{ color: 'white' }}>Primary colored background wrapper.</Text>
77
+ </PageWrapper>
78
+ </div>
79
+ ),
80
+ }
81
+
82
+ export const DifferentPadding: Story = {
83
+ render: () => (
84
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }}>
85
+ <PageWrapper padding="spacing8">
86
+ <Text size="heading03">Small Padding</Text>
87
+ <Text>This wrapper has small padding (8px).</Text>
88
+ </PageWrapper>
89
+
90
+ <PageWrapper padding="spacing16">
91
+ <Text size="heading03">Medium Padding</Text>
92
+ <Text>This wrapper has medium padding (16px).</Text>
93
+ </PageWrapper>
94
+
95
+ <PageWrapper padding="spacing32">
96
+ <Text size="heading03">Large Padding</Text>
97
+ <Text>This wrapper has large padding (32px).</Text>
98
+ </PageWrapper>
99
+ </div>
100
+ ),
101
+ }
102
+
103
+ export const FormLayout: Story = {
104
+ args: {
105
+ width: '600',
106
+ padding: 'spacing24',
107
+ children: (
108
+ <div>
109
+ <Text size="heading02" style={{ marginBottom: '24px' }}>Contact Form</Text>
110
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
111
+ <div>
112
+ <Text tag="label">Name</Text>
113
+ <input style={{ width: '100%', padding: '8px', marginTop: '4px' }} />
114
+ </div>
115
+ <div>
116
+ <Text tag="label">Email</Text>
117
+ <input type="email" style={{ width: '100%', padding: '8px', marginTop: '4px' }} />
118
+ </div>
119
+ <div>
120
+ <Text tag="label">Message</Text>
121
+ <textarea style={{ width: '100%', padding: '8px', marginTop: '4px', minHeight: '100px' }} />
122
+ </div>
123
+ <Button style={{ marginTop: '16px' }}>Submit</Button>
124
+ </div>
125
+ </div>
126
+ ),
127
+ },
128
+ }
129
+
130
+ export const DashboardLayout: Story = {
131
+ args: {
132
+ width: '1200',
133
+ children: (
134
+ <div>
135
+ <Text size="heading01" style={{ marginBottom: '24px' }}>Dashboard</Text>
136
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '16px' }}>
137
+ <div style={{ padding: '16px', border: '1px solid #e0e0e0', borderRadius: '4px' }}>
138
+ <Text size="heading03">Widget 1</Text>
139
+ <Text>Some dashboard content here.</Text>
140
+ </div>
141
+ <div style={{ padding: '16px', border: '1px solid #e0e0e0', borderRadius: '4px' }}>
142
+ <Text size="heading03">Widget 2</Text>
143
+ <Text>More dashboard content here.</Text>
144
+ </div>
145
+ <div style={{ padding: '16px', border: '1px solid #e0e0e0', borderRadius: '4px' }}>
146
+ <Text size="heading03">Widget 3</Text>
147
+ <Text>Additional dashboard content.</Text>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ ),
152
+ },
153
+ }
@@ -0,0 +1,70 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import RadioButton from './RadioButton'
3
+ import { iconMap, iconOptions } from '../../../stories/utils/icons'
4
+
5
+ const meta: Meta<typeof RadioButton> = {
6
+ title: 'Form/RadioButton',
7
+ component: RadioButton,
8
+ parameters: {
9
+ layout: 'centered',
10
+ },
11
+ tags: ['autodocs'],
12
+ argTypes: {
13
+ label: {
14
+ control: 'text',
15
+ description: 'Radio button label'
16
+ },
17
+ description: {
18
+ control: 'text',
19
+ description: 'Radio button description'
20
+ },
21
+ value: {
22
+ control: 'text',
23
+ description: 'Radio button value'
24
+ },
25
+ variant: {
26
+ control: 'select',
27
+ options: ['default', 'box'],
28
+ description: 'Radio button variant'
29
+ },
30
+ checked: {
31
+ control: 'boolean',
32
+ description: 'Whether radio is checked'
33
+ },
34
+ disabled: {
35
+ control: 'boolean',
36
+ description: 'Whether radio is disabled'
37
+ },
38
+ rightIcon: {
39
+ control: 'select',
40
+ options: iconOptions,
41
+ description: 'Right icon',
42
+ },
43
+ },
44
+ }
45
+
46
+ export default meta
47
+ type Story = StoryObj<typeof meta>
48
+
49
+ export const Default: Story = {
50
+ args: {
51
+ label: 'Radio Option',
52
+ value: 'option1',
53
+ rightIcon: 'none',
54
+ },
55
+ render: (args) => {
56
+ const rightIcon = args.rightIcon === 'none' ? undefined : iconMap[args.rightIcon as keyof typeof iconMap]
57
+
58
+ return (
59
+ <RadioButton
60
+ label={args.label}
61
+ value={args.value}
62
+ description={args.description}
63
+ variant={args.variant}
64
+ checked={args.checked}
65
+ disabled={args.disabled}
66
+ rightIcon={rightIcon}
67
+ />
68
+ )
69
+ },
70
+ }
@@ -0,0 +1,44 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import RequiredAsterisk from './RequiredAsterisk'
3
+ import Text from '../Text/Text'
4
+
5
+ const meta: Meta<typeof RequiredAsterisk> = {
6
+ title: 'Data Display/RequiredAsterisk',
7
+ component: RequiredAsterisk,
8
+ parameters: {
9
+ layout: 'centered',
10
+ },
11
+ tags: ['autodocs'],
12
+ }
13
+
14
+ export default meta
15
+ type Story = StoryObj<typeof meta>
16
+
17
+ export const Default: Story = {}
18
+
19
+ export const WithLabel: Story = {
20
+ render: () => (
21
+ <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
22
+ <Text tag="label">Field Label</Text>
23
+ <RequiredAsterisk />
24
+ </div>
25
+ ),
26
+ }
27
+
28
+ export const InForm: Story = {
29
+ render: () => (
30
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
31
+ <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
32
+ <Text tag="label">Required Field</Text>
33
+ <RequiredAsterisk />
34
+ </div>
35
+ <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
36
+ <Text tag="label">Another Required Field</Text>
37
+ <RequiredAsterisk />
38
+ </div>
39
+ <div>
40
+ <Text tag="label">Optional Field</Text>
41
+ </div>
42
+ </div>
43
+ ),
44
+ }
@@ -0,0 +1,182 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { useState } from 'react'
3
+ import SegmentedButton from './SegmentedButton'
4
+
5
+ const meta: Meta<typeof SegmentedButton> = {
6
+ title: 'Form/SegmentedButton',
7
+ component: SegmentedButton,
8
+ parameters: {
9
+ layout: 'centered',
10
+ },
11
+ tags: ['autodocs'],
12
+ argTypes: {
13
+ size: {
14
+ control: 'select',
15
+ options: ['sm', 'md', 'lg'],
16
+ description: 'Size of the segmented button'
17
+ },
18
+ color: {
19
+ control: 'select',
20
+ options: ['primary', 'secondary', 'success', 'warning', 'error'],
21
+ description: 'Color theme of the segmented button'
22
+ },
23
+ disabled: {
24
+ control: 'boolean',
25
+ description: 'Whether the segmented button is disabled'
26
+ }
27
+ },
28
+ }
29
+
30
+ export default meta
31
+ type Story = StoryObj<typeof meta>
32
+
33
+ export const Default: Story = {
34
+ args: {
35
+ options: {
36
+ leftButton: { label: 'Left' },
37
+ rightButton: { label: 'Right' }
38
+ }
39
+ },
40
+ }
41
+
42
+ export const Interactive: Story = {
43
+ render: (args) => {
44
+ const [selected, setSelected] = useState<'left' | 'right'>('left')
45
+
46
+ return (
47
+ <div>
48
+ <SegmentedButton
49
+ {...args}
50
+ options={{
51
+ leftButton: {
52
+ label: 'Option A',
53
+ action: () => setSelected('left')
54
+ },
55
+ rightButton: {
56
+ label: 'Option B',
57
+ action: () => setSelected('right')
58
+ }
59
+ }}
60
+ />
61
+ <p style={{ marginTop: '16px', fontSize: '14px' }}>
62
+ Selected: {selected === 'left' ? 'Option A' : 'Option B'}
63
+ </p>
64
+ </div>
65
+ )
66
+ },
67
+ }
68
+
69
+ export const Sizes: Story = {
70
+ render: () => (
71
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'center' }}>
72
+ <SegmentedButton
73
+ size="sm"
74
+ options={{
75
+ leftButton: { label: 'Small' },
76
+ rightButton: { label: 'Small' }
77
+ }}
78
+ />
79
+ <SegmentedButton
80
+ size="md"
81
+ options={{
82
+ leftButton: { label: 'Medium' },
83
+ rightButton: { label: 'Medium' }
84
+ }}
85
+ />
86
+ <SegmentedButton
87
+ size="lg"
88
+ options={{
89
+ leftButton: { label: 'Large' },
90
+ rightButton: { label: 'Large' }
91
+ }}
92
+ />
93
+ </div>
94
+ ),
95
+ }
96
+
97
+ export const Colors: Story = {
98
+ render: () => (
99
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'center' }}>
100
+ <SegmentedButton
101
+ color="primary"
102
+ options={{
103
+ leftButton: { label: 'Primary' },
104
+ rightButton: { label: 'Primary' }
105
+ }}
106
+ />
107
+ <SegmentedButton
108
+ color="secondary"
109
+ options={{
110
+ leftButton: { label: 'Secondary' },
111
+ rightButton: { label: 'Secondary' }
112
+ }}
113
+ />
114
+ <SegmentedButton
115
+ color="success50"
116
+ options={{
117
+ leftButton: { label: 'Success' },
118
+ rightButton: { label: 'Success' }
119
+ }}
120
+ />
121
+ </div>
122
+ ),
123
+ }
124
+
125
+ export const ViewToggle: Story = {
126
+ render: () => {
127
+ const [, setView] = useState<'list' | 'grid'>('list')
128
+
129
+ return (
130
+ <SegmentedButton
131
+ options={{
132
+ leftButton: {
133
+ label: 'List View',
134
+ action: () => setView('list')
135
+ },
136
+ rightButton: {
137
+ label: 'Grid View',
138
+ action: () => setView('grid')
139
+ }
140
+ }}
141
+ />
142
+ )
143
+ },
144
+ }
145
+
146
+ export const Disabled: Story = {
147
+ args: {
148
+ disabled: true,
149
+ options: {
150
+ leftButton: { label: 'Disabled' },
151
+ rightButton: { label: 'Disabled' }
152
+ }
153
+ },
154
+ }
155
+
156
+ export const YesNo: Story = {
157
+ render: () => {
158
+ const [answer, setAnswer] = useState<'yes' | 'no' | null>(null)
159
+
160
+ return (
161
+ <div>
162
+ <SegmentedButton
163
+ options={{
164
+ leftButton: {
165
+ label: 'Yes',
166
+ action: () => setAnswer('yes')
167
+ },
168
+ rightButton: {
169
+ label: 'No',
170
+ action: () => setAnswer('no')
171
+ }
172
+ }}
173
+ />
174
+ {answer && (
175
+ <p style={{ marginTop: '16px', fontSize: '14px' }}>
176
+ Answer: {answer}
177
+ </p>
178
+ )}
179
+ </div>
180
+ )
181
+ },
182
+ }
@@ -0,0 +1,63 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import Select from './Select'
3
+
4
+ const meta: Meta<typeof Select> = {
5
+ title: 'Form/Select',
6
+ component: Select,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ args: {
12
+ options: [],
13
+ },
14
+ argTypes: {
15
+ label: {
16
+ control: 'text',
17
+ description: 'Select label',
18
+ },
19
+ placeholder: {
20
+ control: 'text',
21
+ description: 'Select placeholder',
22
+ },
23
+ helperText: {
24
+ control: 'text',
25
+ description: 'Helper text below select',
26
+ },
27
+ size: {
28
+ control: 'select',
29
+ options: ['sm', 'md', 'lg'],
30
+ description: 'Select size',
31
+ },
32
+ disabled: {
33
+ control: 'boolean',
34
+ description: 'Whether select is disabled',
35
+ },
36
+ requiredSymbol: {
37
+ control: 'boolean',
38
+ description: 'Show required asterisk',
39
+ },
40
+ options: {
41
+ control: 'object',
42
+ description: 'Options for the select dropdown',
43
+ },
44
+ },
45
+ }
46
+
47
+ export default meta
48
+ type Story = StoryObj<typeof meta>
49
+
50
+ const defaultOptions = [
51
+ { label: 'Option 1', value: 'option1' },
52
+ { label: 'Option 2', value: 'option2' },
53
+ { label: 'Option 3', value: 'option3' },
54
+ { label: 'Option 4', value: 'option4' },
55
+ ]
56
+
57
+ export const Default: Story = {
58
+ args: {
59
+ label: 'Select an option',
60
+ placeholder: 'Choose...',
61
+ options: defaultOptions,
62
+ },
63
+ }