@300codes/design-system 1.0.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 (47) hide show
  1. package/README.md +155 -0
  2. package/package.json +63 -0
  3. package/src/components/BaseIcon/BaseIcon.stories.ts +66 -0
  4. package/src/components/BaseIcon/BaseIcon.vue +96 -0
  5. package/src/components/BaseIcon/index.ts +2 -0
  6. package/src/components/BaseLabel/BaseLabel.stories.ts +114 -0
  7. package/src/components/BaseLabel/BaseLabel.vue +149 -0
  8. package/src/components/BaseLabel/index.ts +2 -0
  9. package/src/components/BaseTooltip/BaseTooltip.stories.ts +113 -0
  10. package/src/components/BaseTooltip/BaseTooltip.vue +123 -0
  11. package/src/components/BaseTooltip/index.ts +2 -0
  12. package/src/components/ButtonWithIcon/ButtonWithIcon.stories.ts +149 -0
  13. package/src/components/ButtonWithIcon/ButtonWithIcon.vue +77 -0
  14. package/src/components/ButtonWithIcon/index.ts +2 -0
  15. package/src/components/CheckboxInput/CheckboxInput.stories.ts +99 -0
  16. package/src/components/CheckboxInput/CheckboxInput.vue +176 -0
  17. package/src/components/CheckboxInput/index.ts +2 -0
  18. package/src/components/LabelInput/LabelInput.vue +111 -0
  19. package/src/components/LabelInput/index.ts +2 -0
  20. package/src/components/RadioInput/RadioInput.stories.ts +114 -0
  21. package/src/components/RadioInput/RadioInput.vue +174 -0
  22. package/src/components/RadioInput/index.ts +2 -0
  23. package/src/components/SearchInput/SearchInput.stories.ts +103 -0
  24. package/src/components/SearchInput/SearchInput.vue +83 -0
  25. package/src/components/SearchInput/index.ts +2 -0
  26. package/src/components/SelectInput/SelectInput.stories.ts +111 -0
  27. package/src/components/SelectInput/SelectInput.vue +497 -0
  28. package/src/components/SelectInput/index.ts +2 -0
  29. package/src/components/SelectInputField/SelectInputField.stories.ts +141 -0
  30. package/src/components/SelectInputField/SelectInputField.vue +64 -0
  31. package/src/components/SelectInputField/index.ts +2 -0
  32. package/src/components/SimpleButton/SimpleButton.stories.ts +143 -0
  33. package/src/components/SimpleButton/SimpleButton.vue +193 -0
  34. package/src/components/SimpleButton/index.ts +2 -0
  35. package/src/components/TabsList/TabsList.stories.ts +83 -0
  36. package/src/components/TabsList/TabsList.vue +156 -0
  37. package/src/components/TabsList/index.ts +2 -0
  38. package/src/components/TextInput/TextInput.stories.ts +125 -0
  39. package/src/components/TextInput/TextInput.vue +273 -0
  40. package/src/components/TextInput/components/InputIconButton.vue +54 -0
  41. package/src/components/TextInput/index.ts +2 -0
  42. package/src/components/TextInputField/TextInputField.stories.ts +133 -0
  43. package/src/components/TextInputField/TextInputField.vue +93 -0
  44. package/src/components/TextInputField/index.ts +2 -0
  45. package/src/components/index.ts +15 -0
  46. package/src/css/tokens.css +417 -0
  47. package/src/types/icon.ts +1 -0
@@ -0,0 +1,176 @@
1
+ <script setup lang="ts">
2
+ import BaseIcon from '../BaseIcon/BaseIcon.vue';
3
+
4
+ export interface CheckboxInputProps {
5
+ name: string;
6
+ id?: string;
7
+ invalid?: boolean;
8
+ required?: boolean;
9
+ disabled?: boolean;
10
+ size?: 'sm' | 'md' | 'lg';
11
+ }
12
+
13
+ withDefaults(defineProps<CheckboxInputProps>(), {
14
+ id: undefined,
15
+ size: 'md',
16
+ });
17
+
18
+ const emit = defineEmits<{
19
+ change: [value: boolean];
20
+ }>();
21
+
22
+ const model = defineModel<boolean>({ required: true });
23
+ </script>
24
+
25
+ <template>
26
+ <div :class="['checkboxInput', `checkboxInput--${size}`]">
27
+ <input
28
+ :id="id || name"
29
+ v-model="model"
30
+ type="checkbox"
31
+ :name="name"
32
+ :disabled="disabled"
33
+ :required="required"
34
+ :aria-required="required"
35
+ :aria-invalid="invalid"
36
+ :aria-disabled="disabled"
37
+ class="sr-only"
38
+ @change="emit('change', model)"
39
+ >
40
+
41
+ <label
42
+ :class="[
43
+ 'checkboxInput__label',
44
+ { 'checkboxInput__label--disabled': disabled },
45
+ 'flex items-center cursor-pointer select-none',
46
+ ]"
47
+ :for="id || name"
48
+ >
49
+ <span
50
+ :class="[
51
+ 'checkboxInput__box',
52
+ {
53
+ 'checkboxInput__box--checked': model,
54
+ 'checkboxInput__box--invalid': invalid,
55
+ },
56
+ 'm-0.5 flex items-center justify-center shrink-0',
57
+ ]"
58
+ >
59
+ <BaseIcon
60
+ v-if="model"
61
+ name="check"
62
+ size="auto"
63
+ class="checkboxInput__check"
64
+ aria-hidden="true"
65
+ />
66
+ </span>
67
+
68
+ <span
69
+ v-if="$slots.default"
70
+ class="checkboxInput__text"
71
+ >
72
+ <slot />
73
+ </span>
74
+ </label>
75
+ </div>
76
+ </template>
77
+
78
+ <style scoped>
79
+ @reference "tailwindcss";
80
+
81
+ .checkboxInput {
82
+ /* md / mobile */
83
+ --_size: var(--checkboxInput-size, 1.5rem);
84
+ --_icon-size: var(--checkboxInput-icon-size, 0.875rem);
85
+ --_radius: var(--checkboxInput-radius, 0.375rem);
86
+ --_fs: var(--checkboxInput-font-size-mobile, 0.875rem);
87
+ --_gap: var(--checkboxInput-spacing, 1rem);
88
+ }
89
+
90
+ @media (min-width: 48rem) {
91
+ .checkboxInput {
92
+ /* md / desktop */
93
+ --_fs: var(--checkboxInput-font-size, 1rem);
94
+ }
95
+ }
96
+
97
+ .checkboxInput--sm {
98
+ --_size: var(--checkboxInput-sm-size, 1.25rem);
99
+ --_icon-size: var(--checkboxInput-sm-icon-size, 0.75rem);
100
+ --_radius: var(--checkboxInput-sm-radius, 0.25rem);
101
+ --_fs: var(--checkboxInput-sm-font-size, 0.875rem);
102
+ --_gap: var(--checkboxInput-sm-spacing, 0.75rem);
103
+ }
104
+
105
+ .checkboxInput--lg {
106
+ --_size: var(--checkboxInput-lg-size, 1.75rem);
107
+ --_icon-size: var(--checkboxInput-lg-icon-size, 1rem);
108
+ --_radius: var(--checkboxInput-lg-radius, 0.5rem);
109
+ --_fs: var(--checkboxInput-lg-font-size, 1.125rem);
110
+ --_gap: var(--checkboxInput-lg-spacing, 1.25rem);
111
+ }
112
+
113
+ /* ── label ───────────────────────────────────────────────────────────────── */
114
+
115
+ .checkboxInput__label {
116
+ gap: var(--_gap);
117
+ font-size: var(--_fs);
118
+ color: var(--input-fg, #0e161b);
119
+ }
120
+
121
+ .checkboxInput__label--disabled {
122
+ color: var(--input-fg-disabled, #89979f);
123
+
124
+ @apply cursor-not-allowed pointer-events-none;
125
+ }
126
+
127
+ /* ── box ─────────────────────────────────────────────────────────────────── */
128
+
129
+ .checkboxInput__box {
130
+ width: var(--_size);
131
+ height: var(--_size);
132
+ border-radius: var(--_radius);
133
+ background-color: var(--input-bg, #ffffff);
134
+ border: var(--checkboxInput-border-width, 1.5px) solid var(--input-border-color, #d6dde1);
135
+ outline: var(--input-outline-width, 4px) solid transparent;
136
+ outline-offset: var(--checkboxInput-outline-offset, -2px);
137
+ }
138
+
139
+ .checkboxInput__label:not(.checkboxInput__label--disabled):hover .checkboxInput__box {
140
+ background-color: var(--input-bg-hover, #f3f5f7);
141
+ border-color: var(--input-border-color-hover, #b0babf);
142
+ }
143
+
144
+ .checkboxInput__box--checked {
145
+ background-color: var(--checkboxInput-checked-bg, #09865e) !important;
146
+ border-color: var(--checkboxInput-checked-bg, #09865e) !important;
147
+ color: var(--checkboxInput-checked-fg, #ffffff);
148
+ }
149
+
150
+ .checkboxInput__box--invalid {
151
+ border-color: var(--input-border-color-invalid, #ef4444);
152
+ }
153
+
154
+ .checkboxInput__check {
155
+ width: var(--_icon-size);
156
+ height: var(--_icon-size);
157
+ }
158
+
159
+ /* ── disabled ────────────────────────────────────────────────────────────── */
160
+
161
+ .checkboxInput__label--disabled .checkboxInput__box {
162
+ background-color: var(--input-bg-disabled, #d6dde1);
163
+ border-color: var(--checkboxInput-border-color-disabled, #89979f);
164
+ }
165
+
166
+ .checkboxInput__label--disabled .checkboxInput__box--checked {
167
+ background-color: var(--checkboxInput-checked-disabled-bg, #89979f) !important;
168
+ border-color: var(--checkboxInput-checked-disabled-bg, #89979f) !important;
169
+ }
170
+
171
+ /* ── focus ───────────────────────────────────────────────────────────────── */
172
+
173
+ input:focus-visible + .checkboxInput__label .checkboxInput__box {
174
+ outline-color: var(--input-outline, #3b82f6);
175
+ }
176
+ </style>
@@ -0,0 +1,2 @@
1
+ export { default as CheckboxInput } from './CheckboxInput.vue';
2
+ export type { CheckboxInputProps } from './CheckboxInput.vue';
@@ -0,0 +1,111 @@
1
+ <script setup lang="ts">
2
+ import BaseIcon from '../BaseIcon/BaseIcon.vue';
3
+
4
+ export interface LabelInputProps {
5
+ name: string;
6
+ label: string;
7
+ message?: string;
8
+ required?: boolean;
9
+ invalid?: boolean | null;
10
+ disabled?: boolean;
11
+ size?: 'sm' | 'md' | 'lg';
12
+ hasStatus?: boolean;
13
+ }
14
+
15
+ withDefaults(defineProps<LabelInputProps>(), {
16
+ message: '',
17
+ size: 'md',
18
+ invalid: null,
19
+ disabled: false,
20
+ hasStatus: true,
21
+ });
22
+ </script>
23
+
24
+ <template>
25
+ <div :class="['labelInput', `labelInput--${size}`, 'flex flex-col']">
26
+ <label
27
+ :for="name"
28
+ :class="['labelInput__label leading-normal', { 'labelInput__label--disabled': disabled }]"
29
+ >
30
+ {{ `${label}${required ? ' *' : ''}` }}
31
+ </label>
32
+
33
+ <div class="labelInput__control relative">
34
+ <slot :disabled="disabled" />
35
+
36
+ <BaseIcon
37
+ v-if="hasStatus && typeof invalid === 'boolean'"
38
+ :name="invalid ? 'error' : 'success'"
39
+ size="auto"
40
+ :class="[
41
+ 'labelInput__status',
42
+ invalid ? 'labelInput__status--invalid' : 'labelInput__status--valid',
43
+ 'absolute top-1/2 right-3 -translate-y-1/2 pointer-events-none',
44
+ ]"
45
+ />
46
+ </div>
47
+
48
+ <span
49
+ v-if="message"
50
+ class="labelInput__message leading-normal"
51
+ >
52
+ {{ message }}
53
+ </span>
54
+ </div>
55
+ </template>
56
+
57
+ <style scoped>
58
+ .labelInput {
59
+ --_icon-size: var(--labelInput-md-status-size, 1.75rem);
60
+ --_fs: var(--labelInput-font-size, 0.8125rem);
61
+ --_msg-fs: var(--labelInput-message-font-size, 0.8125rem);
62
+ --_label-mb: var(--labelInput-label-mb, 0.25rem);
63
+ --_msg-mt: var(--labelInput-message-mt, 0px);
64
+ }
65
+
66
+ .labelInput--sm {
67
+ --_icon-size: var(--labelInput-sm-status-size, 1.5rem);
68
+ --_fs: var(--labelInput-sm-font-size, 0.6875rem);
69
+ --_msg-fs: var(--labelInput-sm-message-font-size, 0.8125rem);
70
+ --_label-mb: var(--labelInput-sm-label-mb, 0.125rem);
71
+ --_msg-mt: var(--labelInput-sm-message-mt, 0px);
72
+ }
73
+
74
+ .labelInput--lg {
75
+ --_icon-size: var(--labelInput-lg-status-size, 2rem);
76
+ --_fs: var(--labelInput-lg-font-size, 0.875rem);
77
+ --_msg-fs: var(--labelInput-lg-message-font-size, 0.875rem);
78
+ --_label-mb: var(--labelInput-lg-label-mb, 0.5rem);
79
+ --_msg-mt: var(--labelInput-lg-message-mt, 1px);
80
+ }
81
+
82
+ .labelInput__label {
83
+ font-size: var(--_fs);
84
+ font-weight: var(--labelInput-font-weight, 400);
85
+ color: var(--labelInput-fg, #0e161b);
86
+ margin-bottom: var(--_label-mb);
87
+ }
88
+
89
+ .labelInput__label--disabled {
90
+ color: var(--labelInput-fg-disabled, #89979f);
91
+ }
92
+
93
+ .labelInput__status--valid {
94
+ color: var(--labelInput-status-valid-fg, #16a34a);
95
+ }
96
+
97
+ .labelInput__status--invalid {
98
+ color: var(--labelInput-status-invalid-fg, #ef4444);
99
+ }
100
+
101
+ .labelInput__status {
102
+ width: var(--_icon-size);
103
+ height: var(--_icon-size);
104
+ }
105
+
106
+ .labelInput__message {
107
+ color: var(--labelInput-message-fg, #ef4444);
108
+ font-size: var(--_msg-fs);
109
+ margin-top: var(--_msg-mt);
110
+ }
111
+ </style>
@@ -0,0 +1,2 @@
1
+ export { default as LabelInput } from './LabelInput.vue';
2
+ export type { LabelInputProps } from './LabelInput.vue';
@@ -0,0 +1,114 @@
1
+ import { ref } from 'vue';
2
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
3
+ import type { ConcreteComponent } from 'vue';
4
+ import type { RadioInputProps } from './RadioInput.vue';
5
+ import RadioInput from './RadioInput.vue';
6
+
7
+ const meta: Meta<RadioInputProps> = {
8
+ title: 'Form/RadioInput',
9
+ component: RadioInput as unknown as ConcreteComponent<RadioInputProps>,
10
+ tags: ['autodocs'],
11
+ argTypes: {
12
+ size: { control: 'select', options: ['sm', 'md', 'lg'] },
13
+ invalid: { control: 'boolean' },
14
+ required: { control: 'boolean' },
15
+ disabled: { control: 'boolean' },
16
+ },
17
+ };
18
+
19
+ export default meta;
20
+ type Story = StoryObj<RadioInputProps>;
21
+
22
+ export const Default: Story = {
23
+ args: { name: 'plan', value: 'basic', size: 'md' },
24
+ render: (args: RadioInputProps) => ({
25
+ components: { RadioInput },
26
+ setup() {
27
+ const model = ref('');
28
+ return { args, model };
29
+ },
30
+ template: '<RadioInput v-bind="args" v-model="model">Basic</RadioInput>',
31
+ }),
32
+ };
33
+
34
+ export const Selected: Story = {
35
+ args: { name: 'plan-selected', value: 'pro', size: 'md' },
36
+ render: (args: RadioInputProps) => ({
37
+ components: { RadioInput },
38
+ setup() {
39
+ const model = ref('pro');
40
+ return { args, model };
41
+ },
42
+ template: '<RadioInput v-bind="args" v-model="model">Pro</RadioInput>',
43
+ }),
44
+ };
45
+
46
+ export const Invalid: Story = {
47
+ args: { name: 'plan-invalid', value: 'basic', size: 'md', invalid: true },
48
+ render: (args: RadioInputProps) => ({
49
+ components: { RadioInput },
50
+ setup() {
51
+ const model = ref('');
52
+ return { args, model };
53
+ },
54
+ template: '<RadioInput v-bind="args" v-model="model">Wymagany wybór</RadioInput>',
55
+ }),
56
+ };
57
+
58
+ export const Disabled: Story = {
59
+ args: { name: 'plan-disabled', value: 'basic', size: 'md', disabled: true },
60
+ render: (args: RadioInputProps) => ({
61
+ components: { RadioInput },
62
+ setup() {
63
+ const model = ref('');
64
+ return { args, model };
65
+ },
66
+ template: '<RadioInput v-bind="args" v-model="model">Niedostępne</RadioInput>',
67
+ }),
68
+ };
69
+
70
+ export const DisabledChecked: Story = {
71
+ args: { name: 'plan-disabled-checked', value: 'basic', size: 'md', disabled: true },
72
+ render: (args: RadioInputProps) => ({
73
+ components: { RadioInput },
74
+ setup() {
75
+ const model = ref('basic');
76
+ return { args, model };
77
+ },
78
+ template: '<RadioInput v-bind="args" v-model="model">Niedostępne (zaznaczone)</RadioInput>',
79
+ }),
80
+ };
81
+
82
+ export const Group: Story = {
83
+ render: () => ({
84
+ components: { RadioInput },
85
+ setup() {
86
+ const selected = ref('');
87
+ return { selected };
88
+ },
89
+ template: `
90
+ <div class="flex flex-col gap-3">
91
+ <RadioInput name="group" value="basic" size="md" v-model="selected">Basic</RadioInput>
92
+ <RadioInput name="group" value="pro" size="md" v-model="selected">Pro</RadioInput>
93
+ <RadioInput name="group" value="enterprise" size="md" v-model="selected">Enterprise</RadioInput>
94
+ </div>
95
+ `,
96
+ }),
97
+ };
98
+
99
+ export const Sizes: Story = {
100
+ render: () => ({
101
+ components: { RadioInput },
102
+ setup() {
103
+ const selected = ref('');
104
+ return { selected };
105
+ },
106
+ template: `
107
+ <div class="flex flex-col gap-4">
108
+ <RadioInput name="sizes" value="sm" size="sm" v-model="selected">Small (24px)</RadioInput>
109
+ <RadioInput name="sizes" value="md" size="md" v-model="selected">Medium (28px)</RadioInput>
110
+ <RadioInput name="sizes" value="lg" size="lg" v-model="selected">Large (32px)</RadioInput>
111
+ </div>
112
+ `,
113
+ }),
114
+ };
@@ -0,0 +1,174 @@
1
+ <script setup lang="ts">
2
+ export interface RadioInputProps {
3
+ name: string;
4
+ value: string;
5
+ id?: string;
6
+ invalid?: boolean;
7
+ required?: boolean;
8
+ disabled?: boolean;
9
+ size?: 'sm' | 'md' | 'lg';
10
+ }
11
+
12
+ withDefaults(defineProps<RadioInputProps>(), {
13
+ id: undefined,
14
+ size: 'md',
15
+ });
16
+
17
+ const emit = defineEmits<{
18
+ change: [value: string];
19
+ }>();
20
+
21
+ const model = defineModel<string>({ required: true });
22
+ </script>
23
+
24
+ <template>
25
+ <div :class="['radioInput', `radioInput--${size}`]">
26
+ <input
27
+ :id="id || `${name}-${value}`"
28
+ v-model="model"
29
+ type="radio"
30
+ :name="name"
31
+ :value="value"
32
+ :disabled="disabled"
33
+ :required="required"
34
+ :aria-required="required"
35
+ :aria-invalid="invalid"
36
+ :aria-disabled="disabled"
37
+ class="sr-only"
38
+ @change="emit('change', model)"
39
+ >
40
+ <label
41
+ :class="[
42
+ 'radioInput__label',
43
+ { 'radioInput__label--disabled': disabled },
44
+ 'flex items-center cursor-pointer select-none',
45
+ ]"
46
+ :for="id || `${name}-${value}`"
47
+ >
48
+ <span
49
+ :class="[
50
+ 'radioInput__box',
51
+ {
52
+ 'radioInput__box--checked': model === value,
53
+ 'radioInput__box--invalid': invalid,
54
+ },
55
+ 'm-0.5 flex items-center justify-center shrink-0',
56
+ ]"
57
+ >
58
+ <span
59
+ v-if="model === value"
60
+ class="radioInput__dot"
61
+ />
62
+ </span>
63
+
64
+ <span
65
+ v-if="$slots.default"
66
+ class="radioInput__text"
67
+ >
68
+ <slot />
69
+ </span>
70
+ </label>
71
+ </div>
72
+ </template>
73
+
74
+ <style scoped>
75
+ @reference "tailwindcss";
76
+
77
+ .radioInput {
78
+ /* md / mobile */
79
+ --_size: var(--radioInput-size, 1.5rem);
80
+ --_fs: var(--radioInput-font-size-mobile, 0.875rem);
81
+ --_gap: var(--radioInput-spacing, 1rem);
82
+ --_dot-size: var(--radioInput-dot-size, 0.875rem);
83
+ }
84
+
85
+ @media (min-width: 48rem) {
86
+ .radioInput {
87
+ /* md / desktop */
88
+ --_fs: var(--radioInput-font-size, 1rem);
89
+ }
90
+ }
91
+
92
+ .radioInput--sm {
93
+ --_size: var(--radioInput-sm-size, 1.25rem);
94
+ --_fs: var(--radioInput-sm-font-size, 0.875rem);
95
+ --_gap: var(--radioInput-sm-spacing, 0.75rem);
96
+ --_dot-size: var(--radioInput-sm-dot-size, 0.875rem);
97
+ }
98
+
99
+ .radioInput--lg {
100
+ --_size: var(--radioInput-lg-size, 1.75rem);
101
+ --_fs: var(--radioInput-lg-font-size, 1.125rem);
102
+ --_gap: var(--radioInput-lg-spacing, 1.25rem);
103
+ --_dot-size: var(--radioInput-lg-dot-size, 1rem);
104
+ }
105
+
106
+ /* ── label ───────────────────────────────────────────────────────────────── */
107
+
108
+ .radioInput__label {
109
+ gap: var(--_gap);
110
+ font-size: var(--_fs);
111
+ color: var(--input-fg, #0e161b);
112
+ }
113
+
114
+ .radioInput__label--disabled {
115
+ color: var(--input-fg-disabled, #89979f);
116
+
117
+ @apply cursor-not-allowed pointer-events-none;
118
+ }
119
+
120
+ /* ── box ─────────────────────────────────────────────────────────────────── */
121
+
122
+ .radioInput__box {
123
+ width: var(--_size);
124
+ height: var(--_size);
125
+ border-radius: 50%;
126
+ background-color: var(--input-bg, #ffffff);
127
+ border: var(--checkboxInput-border-width, 1.5px) solid var(--input-border-color, #d6dde1);
128
+ outline: var(--input-outline-width, 4px) solid transparent;
129
+ outline-offset: var(--checkboxInput-outline-offset, -2px);
130
+ }
131
+
132
+ .radioInput__label:not(.radioInput__label--disabled):hover .radioInput__box {
133
+ background-color: var(--input-bg-hover, #f3f5f7);
134
+ border-color: var(--input-border-color-hover, #b0babf);
135
+ }
136
+
137
+ .radioInput__box--checked {
138
+ background-color: var(--radioInput-checked-bg, #ffffff) !important;
139
+ border-color: var(--radioInput-checked-border, #09865e) !important;
140
+ }
141
+
142
+ .radioInput__box--invalid {
143
+ border-color: var(--input-border-color-invalid, #ef4444);
144
+ }
145
+
146
+ .radioInput__dot {
147
+ width: var(--_dot-size);
148
+ height: var(--_dot-size);
149
+ background-color: var(--radioInput-checked-dot, #09865e);
150
+
151
+ @apply rounded-full;
152
+ }
153
+
154
+ /* ── disabled ────────────────────────────────────────────────────────────── */
155
+
156
+ .radioInput__label--disabled .radioInput__box {
157
+ background-color: var(--input-bg-disabled, #d6dde1);
158
+ border-color: var(--checkboxInput-border-color-disabled, #89979f);
159
+ }
160
+
161
+ .radioInput__label--disabled .radioInput__box--checked {
162
+ background-color: var(--radioInput-checked-disabled-bg, #ffffff) !important;
163
+ }
164
+
165
+ .radioInput__label--disabled .radioInput__dot {
166
+ background-color: var(--radioInput-checked-disabled-dot, #89979f);
167
+ }
168
+
169
+ /* ── focus ───────────────────────────────────────────────────────────────── */
170
+
171
+ input:focus-visible + .radioInput__label .radioInput__box {
172
+ outline-color: var(--input-outline, #3b82f6);
173
+ }
174
+ </style>
@@ -0,0 +1,2 @@
1
+ export { default as RadioInput } from './RadioInput.vue';
2
+ export type { RadioInputProps } from './RadioInput.vue';
@@ -0,0 +1,103 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import type { ConcreteComponent } from 'vue';
3
+ import { ref } from 'vue';
4
+ import type { SearchInputProps } from './SearchInput.vue';
5
+ import SearchInput from './SearchInput.vue';
6
+
7
+ const meta: Meta<SearchInputProps> = {
8
+ title: 'Form/SearchInput',
9
+ component: SearchInput as unknown as ConcreteComponent<SearchInputProps>,
10
+ tags: ['autodocs'],
11
+ argTypes: {
12
+ size: { control: 'select', options: ['sm', 'md', 'lg'] },
13
+ disabled: { control: 'boolean' },
14
+ required: { control: 'boolean' },
15
+ },
16
+ decorators: [
17
+ () => ({ template: '<div style="max-width: 560px"><story /></div>' }),
18
+ ],
19
+ };
20
+
21
+ export default meta;
22
+ type Story = StoryObj<SearchInputProps>;
23
+
24
+ export const Default: Story = {
25
+ args: {
26
+ name: 'search',
27
+ placeholder: 'Search...',
28
+ size: 'md',
29
+ },
30
+ render: (args: SearchInputProps) => ({
31
+ components: { SearchInput },
32
+ setup() {
33
+ const value = ref('');
34
+ return { args, value };
35
+ },
36
+ template: '<SearchInput v-model="value" v-bind="args" />',
37
+ }),
38
+ };
39
+
40
+ export const WithValue: Story = {
41
+ args: {
42
+ name: 'search',
43
+ placeholder: 'Search...',
44
+ size: 'md',
45
+ },
46
+ render: (args: SearchInputProps) => ({
47
+ components: { SearchInput },
48
+ setup() {
49
+ const value = ref('Hello world');
50
+ return { args, value };
51
+ },
52
+ template: '<SearchInput v-model="value" v-bind="args" />',
53
+ }),
54
+ };
55
+
56
+ export const Small: Story = {
57
+ args: {
58
+ name: 'search-sm',
59
+ placeholder: 'Search...',
60
+ size: 'sm',
61
+ },
62
+ render: (args: SearchInputProps) => ({
63
+ components: { SearchInput },
64
+ setup() {
65
+ const value = ref('');
66
+ return { args, value };
67
+ },
68
+ template: '<SearchInput v-model="value" v-bind="args" />',
69
+ }),
70
+ };
71
+
72
+ export const Large: Story = {
73
+ args: {
74
+ name: 'search-lg',
75
+ placeholder: 'Search...',
76
+ size: 'lg',
77
+ },
78
+ render: (args: SearchInputProps) => ({
79
+ components: { SearchInput },
80
+ setup() {
81
+ const value = ref('');
82
+ return { args, value };
83
+ },
84
+ template: '<SearchInput v-model="value" v-bind="args" />',
85
+ }),
86
+ };
87
+
88
+ export const Disabled: Story = {
89
+ args: {
90
+ name: 'search-disabled',
91
+ placeholder: 'Search...',
92
+ size: 'md',
93
+ disabled: true,
94
+ },
95
+ render: (args: SearchInputProps) => ({
96
+ components: { SearchInput },
97
+ setup() {
98
+ const value = ref('');
99
+ return { args, value };
100
+ },
101
+ template: '<SearchInput v-model="value" v-bind="args" />',
102
+ }),
103
+ };