@citizenplane/pimp 16.0.3 → 16.2.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 (71) hide show
  1. package/dist/pimp.es.js +781 -757
  2. package/dist/pimp.umd.js +21 -21
  3. package/dist/style.css +1 -1
  4. package/package.json +10 -8
  5. package/src/components/CpDate.vue +3 -1
  6. package/src/components/CpHeading.vue +4 -5
  7. package/src/components/CpMultiselect.vue +2 -5
  8. package/src/components/CpTable.vue +4 -2
  9. package/src/components/CpTelInput.vue +18 -12
  10. package/src/components/CpText.vue +141 -0
  11. package/src/components/CpToast.vue +1 -1
  12. package/src/components/CpTransitionExpand.vue +23 -20
  13. package/src/components/index.ts +2 -0
  14. package/src/libs/CoreDatepicker.vue +1 -0
  15. package/src/stories/BaseInputLabel.stories.ts +36 -9
  16. package/src/stories/Colors.mdx +9 -0
  17. package/src/stories/Colors.stories.ts +177 -0
  18. package/src/stories/CpAccordion.stories.ts +188 -159
  19. package/src/stories/CpAccordionGroup.stories.ts +51 -95
  20. package/src/stories/CpAirlineLogo.stories.ts +52 -28
  21. package/src/stories/CpAlert.stories.ts +196 -159
  22. package/src/stories/CpBadge.stories.ts +260 -194
  23. package/src/stories/CpButton.stories.ts +257 -426
  24. package/src/stories/CpCheckbox.stories.ts +102 -30
  25. package/src/stories/CpContextualMenu.stories.ts +14 -9
  26. package/src/stories/CpDate.stories.ts +53 -26
  27. package/src/stories/CpDatepicker.stories.ts +53 -80
  28. package/src/stories/CpDialog.stories.ts +23 -2
  29. package/src/stories/CpHeading.stories.ts +60 -20
  30. package/src/stories/CpIcon.stories.ts +98 -31
  31. package/src/stories/CpInput.stories.ts +164 -73
  32. package/src/stories/CpItemActions.stories.ts +23 -12
  33. package/src/stories/CpLoader.stories.ts +55 -7
  34. package/src/stories/CpMenuItem.stories.ts +53 -27
  35. package/src/stories/CpMultiselect.stories.ts +53 -72
  36. package/src/stories/CpPartnerBadge.stories.ts +58 -76
  37. package/src/stories/CpRadio.stories.ts +45 -49
  38. package/src/stories/CpRadioGroup.stories.ts +47 -40
  39. package/src/stories/CpSelect.stories.ts +108 -34
  40. package/src/stories/CpSelectMenu.stories.ts +66 -55
  41. package/src/stories/CpSelectableButton.stories.ts +170 -81
  42. package/src/stories/CpSwitch.stories.ts +136 -134
  43. package/src/stories/CpTable.stories.ts +69 -13
  44. package/src/stories/CpTableEmptyState.stories.ts +11 -7
  45. package/src/stories/CpTabs.stories.ts +23 -5
  46. package/src/stories/CpTelInput.stories.ts +28 -19
  47. package/src/stories/CpText.stories.ts +131 -0
  48. package/src/stories/CpTextarea.stories.ts +74 -27
  49. package/src/stories/CpToast.stories.ts +75 -109
  50. package/src/stories/CpTooltip.stories.ts +82 -77
  51. package/src/stories/CpTransitionCounter.stories.ts +5 -1
  52. package/src/stories/CpTransitionExpand.stories.ts +12 -7
  53. package/src/stories/CpTransitionListItems.stories.ts +6 -1
  54. package/src/stories/CpTransitionSize.stories.ts +9 -1
  55. package/src/stories/CpTransitionSlide.stories.ts +5 -1
  56. package/src/stories/CpTransitionTabContent.stories.ts +5 -1
  57. package/src/stories/Dimensions.mdx +9 -0
  58. package/src/stories/Dimensions.stories.ts +119 -0
  59. package/src/stories/Easings.mdx +9 -0
  60. package/src/stories/Easings.stories.ts +101 -0
  61. package/src/stories/FocusRings.mdx +9 -0
  62. package/src/stories/FocusRings.stories.ts +74 -0
  63. package/src/stories/Shadows.mdx +9 -0
  64. package/src/stories/Shadows.stories.ts +100 -0
  65. package/src/stories/Typography.mdx +9 -0
  66. package/src/stories/Typography.stories.ts +181 -0
  67. package/src/stories/documentationStyles.ts +2 -10
  68. package/src/stories/tokenUtils.ts +259 -0
  69. package/src/types/primevue-toasteventbus.d.ts +14 -0
  70. package/tsconfig.json +1 -0
  71. package/.lintstagedrc.json +0 -4
@@ -1,67 +1,107 @@
1
- import type { Meta, StoryObj } from '@storybook/vue3'
1
+ import type { Args, Meta, StoryObj } from '@storybook/vue3-vite'
2
2
 
3
3
  import CpHeading from '@/components/CpHeading.vue'
4
4
 
5
+ import { HeadingLevels } from '@/constants'
6
+ import { docLabelStyle } from '@/stories/documentationStyles'
7
+
8
+ const headingLevels = Object.values(HeadingLevels)
9
+ const headingSizes = [100, 200, 300, 400, 500, 600, 700, 800, 900] as const
10
+
11
+ const headingRowStyle = 'display: grid; grid-template-columns: 90px 1fr; gap: 16px; align-items: center;'
12
+
5
13
  const meta = {
6
- title: 'Visual/CpHeading',
14
+ title: 'Atoms/CpHeading',
7
15
  component: CpHeading,
16
+ tags: ['deprecated'],
17
+ parameters: {
18
+ docs: {
19
+ description: {
20
+ component: '> ⚠️ **Deprecated.** `CpHeading` is kept for backwards compatibility only. Use CpText instead.',
21
+ },
22
+ },
23
+ },
8
24
  argTypes: {
9
25
  headingLevel: {
10
26
  control: 'select',
11
- options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
27
+ options: headingLevels,
12
28
  description: 'HTML heading level',
13
29
  },
14
30
  size: {
15
31
  control: 'select',
16
- options: [100, 200, 300, 400, 500, 600, 700, 800, 900],
32
+ options: headingSizes,
17
33
  description: 'Size variant of the heading',
18
34
  },
19
35
  },
20
36
  } satisfies Meta<typeof CpHeading>
21
37
 
22
38
  export default meta
39
+
23
40
  type Story = StoryObj<typeof meta>
24
41
 
42
+ /**
43
+ * @deprecated Default heading. Prefer native `<h1>`–`<h6>` styled with typography CSS variables.
44
+ */
25
45
  export const Default: Story = {
26
46
  args: {
27
- headingLevel: 'h1',
47
+ headingLevel: HeadingLevels.H1,
28
48
  size: 500,
29
49
  },
30
- render: (args) => ({
50
+ render: (args: Args) => ({
31
51
  components: { CpHeading },
32
52
  setup() {
33
53
  return { args }
34
54
  },
35
55
  template: `
36
56
  <div style="padding: 20px;">
37
- <CpHeading v-bind="args">
38
- Default Heading
39
- </CpHeading>
57
+ <CpHeading v-bind="args">Default Heading</CpHeading>
40
58
  </div>
41
59
  `,
42
60
  }),
43
61
  }
44
62
 
45
- export const AllSizes: Story = {
63
+ /* -------------------------------------------------------------------------- */
64
+ /* Sizes */
65
+ /* -------------------------------------------------------------------------- */
66
+
67
+ /**
68
+ * @deprecated All sizes stacked from `100` (smallest) to `900` (largest). The
69
+ * visual size is decoupled from the semantic HTML level.
70
+ */
71
+ export const Sizes: Story = {
72
+ parameters: { controls: { disable: true } },
46
73
  render: () => ({
47
74
  components: { CpHeading },
75
+ setup() {
76
+ return {
77
+ headingSizes,
78
+ headingRowStyle,
79
+ docLabelStyle,
80
+ }
81
+ },
48
82
  template: `
49
83
  <div style="padding: 20px; display: flex; flex-direction: column; gap: 16px;">
50
- <CpHeading size="100">Size 100 Heading</CpHeading>
51
- <CpHeading size="200">Size 200 Heading</CpHeading>
52
- <CpHeading size="300">Size 300 Heading</CpHeading>
53
- <CpHeading size="400">Size 400 Heading</CpHeading>
54
- <CpHeading size="500">Size 500 Heading</CpHeading>
55
- <CpHeading size="600">Size 600 Heading</CpHeading>
56
- <CpHeading size="700">Size 700 Heading</CpHeading>
57
- <CpHeading size="800">Size 800 Heading</CpHeading>
58
- <CpHeading size="900">Size 900 Heading</CpHeading>
84
+ <div v-for="size in headingSizes" :key="size" :style="headingRowStyle">
85
+ <span :style="docLabelStyle">{{ size }}</span>
86
+ <CpHeading :size="size">Size {{ size }} Heading</CpHeading>
87
+ </div>
59
88
  </div>
60
89
  `,
61
90
  }),
62
91
  }
63
92
 
64
- export const SizeAndLevelCombination: Story = {
93
+ /* -------------------------------------------------------------------------- */
94
+ /* Semantic level */
95
+ /* -------------------------------------------------------------------------- */
96
+
97
+ /**
98
+ * @deprecated The recommended pairing between semantic HTML level and visual
99
+ * size — but any combination is valid. Picking the right `headingLevel`
100
+ * matters for accessibility and document outline, while `size` controls the
101
+ * look.
102
+ */
103
+ export const LevelAndSizePairing: Story = {
104
+ parameters: { controls: { disable: true } },
65
105
  render: () => ({
66
106
  components: { CpHeading },
67
107
  template: `
@@ -1,8 +1,10 @@
1
- import type { Meta, StoryObj } from '@storybook/vue3'
1
+ import type { Args, Meta, StoryObj } from '@storybook/vue3-vite'
2
2
 
3
3
  import CpIcon from '@/components/CpIcon.vue'
4
4
 
5
5
  import { CustomCpIcons } from '@/constants'
6
+ import { docCellStyle, docLabelStyle, docRowWrapStyle } from '@/stories/documentationStyles'
7
+ import { copyableClass, copyableCopiedClass, useCopier } from '@/stories/tokenUtils'
6
8
 
7
9
  const iconsOptions = [
8
10
  ...Object.keys(CustomCpIcons),
@@ -29,9 +31,12 @@ const iconsOptions = [
29
31
  'invalid-icon-name',
30
32
  ].sort()
31
33
 
34
+ const iconSizes = [16, 24, 32, 48] as const
35
+
32
36
  const meta = {
33
- title: 'Visual/CpIcon',
37
+ title: 'Foundations/CpIcon',
34
38
  component: CpIcon,
39
+ tags: ['!dev'],
35
40
  argTypes: {
36
41
  type: {
37
42
  control: 'select',
@@ -52,13 +57,16 @@ const meta = {
52
57
  export default meta
53
58
  type Story = StoryObj<typeof meta>
54
59
 
60
+ /**
61
+ * Default icon. Use the controls to experiment with each prop in isolation.
62
+ */
55
63
  export const Default: Story = {
56
64
  args: {
57
65
  type: 'check',
58
66
  size: 24,
59
67
  tag: 'i',
60
68
  },
61
- render: (args) => ({
69
+ render: (args: Args) => ({
62
70
  components: { CpIcon },
63
71
  setup() {
64
72
  return { args }
@@ -71,42 +79,76 @@ export const Default: Story = {
71
79
  }),
72
80
  }
73
81
 
74
- export const DifferentSizes: Story = {
75
- args: {
76
- type: 'check',
77
- },
78
- render: (args) => ({
82
+ /* -------------------------------------------------------------------------- */
83
+ /* Sizes */
84
+ /* -------------------------------------------------------------------------- */
85
+
86
+ /**
87
+ * The icon scales freely through the `size` prop (pixel value). Below are
88
+ * the most common sizes used across the design system.
89
+ */
90
+ export const Sizes: Story = {
91
+ args: { type: 'check' },
92
+ parameters: { controls: { disable: true } },
93
+ render: () => ({
79
94
  components: { CpIcon },
80
95
  setup() {
81
- return { args }
96
+ return { iconSizes, docCellStyle, docLabelStyle, docRowWrapStyle }
82
97
  },
83
98
  template: `
84
- <div style="padding: 20px; display: flex; align-items: center; gap: 16px;">
85
- <CpIcon v-bind="{ ...args, size: 16 }" />
86
- <CpIcon v-bind="{ ...args, size: 24 }" />
87
- <CpIcon v-bind="{ ...args, size: 32 }" />
88
- <CpIcon v-bind="{ ...args, size: 48 }" />
99
+ <div :style="docRowWrapStyle">
100
+ <div v-for="size in iconSizes" :key="size" :style="docCellStyle">
101
+ <span :style="docLabelStyle">{{ size }}px</span>
102
+ <CpIcon type="check" :size="size" />
103
+ </div>
89
104
  </div>
90
105
  `,
91
106
  }),
92
107
  }
93
108
 
109
+ /* -------------------------------------------------------------------------- */
110
+ /* Catalog */
111
+ /* -------------------------------------------------------------------------- */
112
+
113
+ /**
114
+ * Every available icon rendered on a neutral surface. Hover a cell to see
115
+ * the icon name.
116
+ */
94
117
  export const AllIcons: Story = {
95
- args: {
96
- type: 'check',
97
- },
118
+ args: { type: 'check' },
119
+ parameters: { controls: { disable: true } },
98
120
  render: () => ({
99
121
  components: { CpIcon },
100
122
  setup() {
101
- return { icons: iconsOptions }
123
+ const { copiedKey, copy } = useCopier()
124
+ return {
125
+ icons: iconsOptions,
126
+ copiedKey,
127
+ copy,
128
+ copyClass: copyableClass,
129
+ copiedClass: copyableCopiedClass,
130
+ }
102
131
  },
103
132
  template: `
104
133
  <div style="padding: 20px; display: grid; grid-template-columns: repeat(4, minmax(100px, 3fr)); gap: 16px;">
105
- <div v-for="icon in icons" style="display: flex; flex-direction: column; align-items: center;border: 1px solid #eee;border-radius: 4px;">
106
- <span style="font-size: 12px; padding: 8px; word-break: break-word;">{{ icon }}</span>
107
- <hr style="width: 100%; background-color: #eee; height: 1px; border: 0" />
134
+ <div
135
+ v-for="icon in icons"
136
+ :key="icon"
137
+ style="display: flex; flex-direction: column; align-items: center; border: 1px solid #eee; border-radius: 4px;"
138
+ >
139
+ <span
140
+ style="font-size: 12px; padding: 8px; word-break: break-word;"
141
+ :class="[copyClass, copiedKey === icon ? copiedClass : '']"
142
+ role="button"
143
+ tabindex="0"
144
+ :title="'Click to copy ' + icon"
145
+ @click="copy(icon)"
146
+ @keydown.enter.prevent="copy(icon)"
147
+ @keydown.space.prevent="copy(icon)"
148
+ >{{ copiedKey === icon ? 'Copied!' : icon }}</span>
149
+ <hr style="width: 100%; background-color: #eee; height: 1px; border: 0;" />
108
150
  <div style="padding: 20px;">
109
- <CpIcon :key="icon" :type="icon" />
151
+ <CpIcon :type="icon" />
110
152
  </div>
111
153
  </div>
112
154
  </div>
@@ -114,22 +156,47 @@ export const AllIcons: Story = {
114
156
  }),
115
157
  }
116
158
 
159
+ /**
160
+ * Same catalog rendered on a colored surface to verify that icons inherit
161
+ * the current text color.
162
+ */
117
163
  export const AllIconsColored: Story = {
118
- args: {
119
- type: 'check',
120
- },
164
+ args: { type: 'check' },
165
+ parameters: { controls: { disable: true } },
121
166
  render: () => ({
122
167
  components: { CpIcon },
123
168
  setup() {
124
- return { icons: iconsOptions }
169
+ const { copiedKey, copy } = useCopier()
170
+ return {
171
+ icons: iconsOptions,
172
+ copiedKey,
173
+ copy,
174
+ copyClass: copyableClass,
175
+ copiedClass: copyableCopiedClass,
176
+ }
125
177
  },
126
178
  template: `
127
179
  <div style="padding: 20px; display: grid; grid-template-columns: repeat(4, minmax(100px, 3fr)); gap: 16px;">
128
- <div v-for="icon in icons" style="display: flex; flex-direction: column; align-items: center;border: 1px solid #eee;border-radius: 4px;">
129
- <span style="font-size: 12px; padding: 8px; word-break: break-word;">{{ icon }}</span>
130
- <hr style="width: 100%; background-color: #eee; height: 1px; border: 0" />
131
- <div style="padding: 20px; color: green;background-color: #f0fdf4;width: 100%;display: flex;align-items: center;justify-content: center;height: 100%;">
132
- <CpIcon :key="icon" :type="icon" />
180
+ <div
181
+ v-for="icon in icons"
182
+ :key="icon"
183
+ style="display: flex; flex-direction: column; align-items: center; border: 1px solid #eee; border-radius: 4px;"
184
+ >
185
+ <span
186
+ style="font-size: 12px; padding: 8px; word-break: break-word;"
187
+ :class="[copyClass, copiedKey === icon ? copiedClass : '']"
188
+ role="button"
189
+ tabindex="0"
190
+ :title="'Click to copy ' + icon"
191
+ @click="copy(icon)"
192
+ @keydown.enter.prevent="copy(icon)"
193
+ @keydown.space.prevent="copy(icon)"
194
+ >{{ copiedKey === icon ? 'Copied!' : icon }}</span>
195
+ <hr style="width: 100%; background-color: #eee; height: 1px; border: 0;" />
196
+ <div
197
+ style="padding: 20px; color: green; background-color: #f0fdf4; width: 100%; display: flex; align-items: center; justify-content: center; height: 100%;"
198
+ >
199
+ <CpIcon :type="icon" />
133
200
  </div>
134
201
  </div>
135
202
  </div>
@@ -1,12 +1,30 @@
1
1
  import { ref } from 'vue'
2
2
 
3
- import type { Meta, StoryObj } from '@storybook/vue3'
3
+ import type { ComponentPropsAndSlots, Meta, StoryObj } from '@storybook/vue3-vite'
4
4
 
5
5
  import CpIcon from '@/components/CpIcon.vue'
6
6
  import CpInput from '@/components/CpInput.vue'
7
7
 
8
+ import { docCellStyle, docLabelStyle, docRowColumnStyle } from '@/stories/documentationStyles'
9
+
10
+ /** Native attributes forwarded to the inner `<input>` (not declared as Vue props). */
11
+ type CpInputPassthroughAttrs = {
12
+ autocomplete?: string
13
+ disabled?: boolean
14
+ placeholder?: string
15
+ required?: boolean
16
+ type?: string
17
+ }
18
+
19
+ type CpInputStoryArgs = ComponentPropsAndSlots<typeof CpInput> & CpInputPassthroughAttrs
20
+
21
+ const inputSizes = ['sm', 'md', 'lg'] as const
22
+ const inputTypes = ['text', 'email', 'password', 'number', 'tel', 'url'] as const
23
+
24
+ const inputStackStyle = `${docCellStyle} width: 100%; max-width: 360px;`
25
+
8
26
  const meta = {
9
- title: 'Form/CpInput',
27
+ title: 'Atoms/CpInput',
10
28
  component: CpInput,
11
29
  argTypes: {
12
30
  modelValue: {
@@ -15,12 +33,12 @@ const meta = {
15
33
  },
16
34
  size: {
17
35
  control: 'select',
18
- options: ['sm', 'md', 'lg'],
36
+ options: inputSizes,
19
37
  description: 'The size of the input',
20
38
  },
21
39
  type: {
22
40
  control: 'select',
23
- options: ['text', 'email', 'password', 'number', 'tel', 'url'],
41
+ options: inputTypes,
24
42
  description: 'The type of input',
25
43
  },
26
44
  label: {
@@ -52,11 +70,19 @@ const meta = {
52
70
  description: 'Autocomplete attribute value',
53
71
  },
54
72
  },
55
- } satisfies Meta<typeof CpInput>
73
+ } satisfies Meta<CpInputStoryArgs>
56
74
 
57
75
  export default meta
58
- type Story = StoryObj<typeof meta>
59
76
 
77
+ // Use explicit args type: `StoryObj<typeof meta>` would re-infer args from `component` only
78
+ // and drop native attributes passed through `$attrs`.
79
+ type Story = StoryObj<CpInputStoryArgs>
80
+
81
+ type StoryArgs = Partial<CpInputStoryArgs>
82
+
83
+ /**
84
+ * Default input. Use the controls to experiment with each prop in isolation.
85
+ */
60
86
  export const Default: Story = {
61
87
  args: {
62
88
  label: 'Input Label',
@@ -72,7 +98,7 @@ export const Default: Story = {
72
98
  isSearch: false,
73
99
  help: '',
74
100
  },
75
- render: (args) => ({
101
+ render: (args: StoryArgs) => ({
76
102
  components: { CpInput },
77
103
  setup() {
78
104
  const value = ref('')
@@ -80,64 +106,127 @@ export const Default: Story = {
80
106
  },
81
107
  template: `
82
108
  <div style="max-width: 400px; padding: 20px;">
83
- <CpInput
84
- v-model="value"
85
- v-bind="args"
86
- />
109
+ <CpInput v-model="value" v-bind="args" />
87
110
  </div>
88
111
  `,
89
112
  }),
90
113
  }
91
114
 
92
- export const WithError: Story = {
93
- args: {
94
- ...Default.args,
95
- isInvalid: true,
96
- errorMessage: 'This field is required',
97
- },
98
- }
115
+ /* -------------------------------------------------------------------------- */
116
+ /* Sizes */
117
+ /* -------------------------------------------------------------------------- */
99
118
 
100
- export const Required: Story = {
101
- args: {
102
- ...Default.args,
103
- required: true,
104
- },
119
+ /**
120
+ * All sizes rendered side by side, from `sm` to `lg`.
121
+ */
122
+ export const Sizes: Story = {
123
+ parameters: { controls: { disable: true } },
124
+ render: () => ({
125
+ components: { CpInput },
126
+ setup() {
127
+ const value = ref('')
128
+ return { value, inputSizes, docRowColumnStyle, inputStackStyle, docLabelStyle }
129
+ },
130
+ template: `
131
+ <div :style="docRowColumnStyle">
132
+ <div v-for="size in inputSizes" :key="size" :style="inputStackStyle">
133
+ <span :style="docLabelStyle">{{ size }}</span>
134
+ <CpInput v-model="value" :size="size" label="Input label" placeholder="Enter text here" />
135
+ </div>
136
+ </div>
137
+ `,
138
+ }),
105
139
  }
106
140
 
107
- export const Disabled: Story = {
108
- args: {
109
- ...Default.args,
110
- disabled: true,
111
- },
112
- }
141
+ /* -------------------------------------------------------------------------- */
142
+ /* States */
143
+ /* -------------------------------------------------------------------------- */
113
144
 
114
- export const Email: Story = {
115
- args: {
116
- ...Default.args,
117
- type: 'email',
118
- label: 'Email Address',
119
- placeholder: 'Enter your email',
120
- },
145
+ /**
146
+ * Default, required, disabled and invalid states compared side by side.
147
+ */
148
+ export const States: Story = {
149
+ parameters: { controls: { disable: true } },
150
+ render: () => ({
151
+ components: { CpInput },
152
+ setup() {
153
+ const value = ref('')
154
+ return { value, docRowColumnStyle, inputStackStyle, docLabelStyle }
155
+ },
156
+ template: `
157
+ <div :style="docRowColumnStyle">
158
+ <div :style="inputStackStyle">
159
+ <span :style="docLabelStyle">Default</span>
160
+ <CpInput v-model="value" label="Input label" placeholder="Enter text here" />
161
+ </div>
162
+ <div :style="inputStackStyle">
163
+ <span :style="docLabelStyle">Required</span>
164
+ <CpInput v-model="value" label="Input label" placeholder="Enter text here" :required="true" />
165
+ </div>
166
+ <div :style="inputStackStyle">
167
+ <span :style="docLabelStyle">Disabled</span>
168
+ <CpInput v-model="value" label="Input label" placeholder="Enter text here" :disabled="true" />
169
+ </div>
170
+ <div :style="inputStackStyle">
171
+ <span :style="docLabelStyle">Invalid</span>
172
+ <CpInput
173
+ v-model="value"
174
+ label="Input label"
175
+ placeholder="Enter text here"
176
+ :is-invalid="true"
177
+ error-message="This field is required"
178
+ />
179
+ </div>
180
+ </div>
181
+ `,
182
+ }),
121
183
  }
122
184
 
123
- export const Password: Story = {
124
- args: {
125
- ...Default.args,
126
- type: 'password',
127
- label: 'Password',
128
- placeholder: 'Enter your password',
129
- },
130
- }
185
+ /* -------------------------------------------------------------------------- */
186
+ /* Types */
187
+ /* -------------------------------------------------------------------------- */
131
188
 
132
- export const Number: Story = {
133
- args: {
134
- ...Default.args,
135
- type: 'number',
136
- label: 'Amount',
137
- placeholder: 'Enter amount',
138
- },
189
+ /**
190
+ * Native HTML input types wired to the appropriate keyboard and validation.
191
+ */
192
+ export const Types: Story = {
193
+ parameters: { controls: { disable: true } },
194
+ render: () => ({
195
+ components: { CpInput },
196
+ setup() {
197
+ const value = ref('')
198
+ return { value, docRowColumnStyle, inputStackStyle, docLabelStyle }
199
+ },
200
+ template: `
201
+ <div :style="docRowColumnStyle">
202
+ <div :style="inputStackStyle">
203
+ <span :style="docLabelStyle">text</span>
204
+ <CpInput v-model="value" type="text" label="Name" placeholder="Your full name" />
205
+ </div>
206
+ <div :style="inputStackStyle">
207
+ <span :style="docLabelStyle">email</span>
208
+ <CpInput v-model="value" type="email" label="Email address" placeholder="you@example.com" />
209
+ </div>
210
+ <div :style="inputStackStyle">
211
+ <span :style="docLabelStyle">password</span>
212
+ <CpInput v-model="value" type="password" label="Password" placeholder="Enter your password" />
213
+ </div>
214
+ <div :style="inputStackStyle">
215
+ <span :style="docLabelStyle">number</span>
216
+ <CpInput v-model="value" type="number" label="Amount" placeholder="Enter amount" />
217
+ </div>
218
+ </div>
219
+ `,
220
+ }),
139
221
  }
140
222
 
223
+ /* -------------------------------------------------------------------------- */
224
+ /* Help & tooltip */
225
+ /* -------------------------------------------------------------------------- */
226
+
227
+ /**
228
+ * Attach a help text under the field and a tooltip next to the label.
229
+ */
141
230
  export const HelpAndTooltip: Story = {
142
231
  args: {
143
232
  ...Default.args,
@@ -146,6 +235,13 @@ export const HelpAndTooltip: Story = {
146
235
  },
147
236
  }
148
237
 
238
+ /* -------------------------------------------------------------------------- */
239
+ /* Variants */
240
+ /* -------------------------------------------------------------------------- */
241
+
242
+ /**
243
+ * Search input with a built-in icon.
244
+ */
149
245
  export const Search: Story = {
150
246
  args: {
151
247
  ...Default.args,
@@ -153,6 +249,9 @@ export const Search: Story = {
153
249
  },
154
250
  }
155
251
 
252
+ /**
253
+ * Input masked with a pattern (phone numbers, card numbers, etc.).
254
+ */
156
255
  export const Mask: Story = {
157
256
  args: {
158
257
  ...Default.args,
@@ -161,11 +260,12 @@ export const Mask: Story = {
161
260
  },
162
261
  }
163
262
 
263
+ /**
264
+ * Slot icons on either side of the input.
265
+ */
164
266
  export const WithIcon: Story = {
165
- args: {
166
- ...Default.args,
167
- },
168
- render: (args) => ({
267
+ args: { ...Default.args },
268
+ render: (args: StoryArgs) => ({
169
269
  components: { CpInput, CpIcon },
170
270
  setup() {
171
271
  const value = ref('')
@@ -173,10 +273,7 @@ export const WithIcon: Story = {
173
273
  },
174
274
  template: `
175
275
  <div style="max-width: 400px; padding: 20px;">
176
- <CpInput
177
- v-model="value"
178
- v-bind="args"
179
- >
276
+ <CpInput v-model="value" v-bind="args">
180
277
  <template #trailing-icon>
181
278
  <CpIcon type="users" />
182
279
  </template>
@@ -189,11 +286,13 @@ export const WithIcon: Story = {
189
286
  }),
190
287
  }
191
288
 
289
+ /**
290
+ * Use the icon slots to render unit labels, perfect for currencies or
291
+ * measurement units.
292
+ */
192
293
  export const WithUnits: Story = {
193
- args: {
194
- ...Default.args,
195
- },
196
- render: (args) => ({
294
+ args: { ...Default.args },
295
+ render: (args: StoryArgs) => ({
197
296
  components: { CpInput },
198
297
  setup() {
199
298
  const value = ref('')
@@ -201,11 +300,7 @@ export const WithUnits: Story = {
201
300
  },
202
301
  template: `
203
302
  <div style="display: flex; flex-direction: column; gap: 10px; max-width: 400px; padding: 20px;">
204
- <CpInput
205
- v-model="value"
206
- mask="####"
207
- v-bind="args"
208
- >
303
+ <CpInput v-model="value" mask="####" v-bind="args">
209
304
  <template #leading-icon>
210
305
 
211
306
  </template>
@@ -214,11 +309,7 @@ export const WithUnits: Story = {
214
309
  </template>
215
310
  </CpInput>
216
311
 
217
- <CpInput
218
- v-model="value"
219
- mask="####"
220
- v-bind="args"
221
- >
312
+ <CpInput v-model="value" mask="####" v-bind="args">
222
313
  <template #leading-icon>
223
314
  kg
224
315
  </template>