@rocketui/vue 0.0.46 → 0.0.47
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.
- package/.eslintrc.cjs +79 -0
- package/.gitattributes +2 -0
- package/.github/workflows/chromatic.yml +28 -0
- package/.github/workflows/publish-storybook.yml +41 -0
- package/.husky/pre-commit +4 -0
- package/.prettierrc.cjs +10 -0
- package/.storybook/Theme.js +17 -0
- package/.storybook/main.ts +20 -0
- package/.storybook/manager-head.html +3 -0
- package/.storybook/manager.js +8 -0
- package/.storybook/preview-head.html +3 -0
- package/.storybook/preview.ts +36 -0
- package/.storybook/source-panel/manager.js +28 -0
- package/.storybook/withSource.js +91 -0
- package/.vscode/extensions.json +11 -0
- package/.vscode/settings.json +20 -0
- package/index.html +13 -0
- package/lib/main.ts +48 -0
- package/package.json +2 -8
- package/postcss.config.cjs +9 -0
- package/resources/rocket-ui-logo-dark.svg +27 -0
- package/resources/rocket-ui-logo-light.svg +27 -0
- package/shims-rocketui.d.ts +9 -0
- package/src/App.vue +15 -0
- package/src/assets/blank-avatar.svg +3 -0
- package/src/assets/icons/mdi.js +7302 -0
- package/src/assets/logo.svg +1 -0
- package/src/components/Accordion/Accordion.mdx +88 -0
- package/src/components/Accordion/Accordion.stories.ts +257 -0
- package/src/components/Accordion/RAccordion.vue +73 -0
- package/src/components/Accordion/accordion.css +75 -0
- package/src/components/Accordion/accordion.spec.ts +123 -0
- package/src/components/Alert/Alert.mdx +120 -0
- package/src/components/Alert/Alert.stories.ts +118 -0
- package/src/components/Alert/RAlert.vue +119 -0
- package/src/components/Alert/alert.css +136 -0
- package/src/components/Alert/alert.spec.ts +32 -0
- package/src/components/Avatar/Avatar.mdx +96 -0
- package/src/components/Avatar/Avatar.stories.ts +65 -0
- package/src/components/Avatar/RAvatar.vue +115 -0
- package/src/components/Avatar/avatar.css +82 -0
- package/src/components/Avatar/avatar.spec.ts +38 -0
- package/src/components/Badge/Badge.mdx +112 -0
- package/src/components/Badge/Badge.stories.ts +99 -0
- package/src/components/Badge/RBadge.vue +89 -0
- package/src/components/Badge/badge.css +63 -0
- package/src/components/Badge/badge.spec.ts +20 -0
- package/src/components/Box/Box.mdx +20 -0
- package/src/components/Box/Box.stories.ts +56 -0
- package/src/components/Box/RBox.vue +97 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.ts +115 -0
- package/src/components/Breadcrumb/RBreadcrumb.vue +43 -0
- package/src/components/Breadcrumb/breadcrumb.css +29 -0
- package/src/components/Button/Button.mdx +148 -0
- package/src/components/Button/Button.spec.ts +29 -0
- package/src/components/Button/Button.stories.ts +118 -0
- package/src/components/Button/RButton.vue +179 -0
- package/src/components/Button/button.css +146 -0
- package/src/components/Checkbox/Checkbox.mdx +100 -0
- package/src/components/Checkbox/Checkbox.stories.ts +67 -0
- package/src/components/Checkbox/RCheckbox.vue +195 -0
- package/src/components/Checkbox/checkbox.css +67 -0
- package/src/components/Checkbox/checkbox.spec.ts +60 -0
- package/src/components/Chips/Chip.mdx +113 -0
- package/src/components/Chips/Chip.stories.ts +122 -0
- package/src/components/Chips/RChip.vue +125 -0
- package/src/components/Chips/chip.css +62 -0
- package/src/components/Chips/chip.spec.ts +40 -0
- package/src/components/Dropdown/Dropdown.mdx +135 -0
- package/src/components/Dropdown/Dropdown.stories.ts +84 -0
- package/src/components/Dropdown/RDropdown.vue +392 -0
- package/src/components/Dropdown/dropdown.css +113 -0
- package/src/components/Dropdown/dropdown.spec.ts +98 -0
- package/src/components/Flex/Flex.mdx +20 -0
- package/src/components/Flex/Flex.stories.js +127 -0
- package/src/components/Flex/RFlex.vue +91 -0
- package/src/components/Grid/Grid.mdx +20 -0
- package/src/components/Grid/Grid.stories.js +107 -0
- package/src/components/Grid/RGrid.vue +138 -0
- package/src/components/Icon/Icon.mdx +68 -0
- package/src/components/Icon/Icon.stories.ts +33 -0
- package/src/components/Icon/RIcon.vue +56 -0
- package/src/components/Icon/icon.spec.ts +25 -0
- package/src/components/ItemGroup/ItemGroup.stories.ts +91 -0
- package/src/components/ItemGroup/RItem.vue +74 -0
- package/src/components/ItemGroup/RItemGroup.vue +122 -0
- package/src/components/ItemGroup/__snapshots__/itemgroup.spec.ts.snap +13 -0
- package/src/components/ItemGroup/itemgroup.spec.ts +67 -0
- package/src/components/Label/Label.mdx +50 -0
- package/src/components/Label/Label.stories.ts +38 -0
- package/src/components/Label/RLabel.vue +42 -0
- package/src/components/Label/label.css +0 -0
- package/src/components/Modal/Modal.mdx +91 -0
- package/src/components/Modal/Modal.stories.ts +125 -0
- package/src/components/Modal/RModal.vue +130 -0
- package/src/components/Modal/modal.css +41 -0
- package/src/components/Modal/modal.spec.ts +25 -0
- package/src/components/Pagination/Pagination.stories.ts +24 -0
- package/src/components/Pagination/RPagination.vue +103 -0
- package/src/components/Pagination/pagination.css +47 -0
- package/src/components/Pagination/pagination.spec.ts +17 -0
- package/src/components/ProgressBar/ProgressBar.stories.ts +34 -0
- package/src/components/ProgressBar/RProgressBar.vue +21 -0
- package/src/components/ProgressBar/progressbar.css +24 -0
- package/src/components/ProgressBar/progressbar.spec.ts +17 -0
- package/src/components/Shared/Enums.ts +1 -0
- package/src/components/Sidebar/RSidebar.vue +27 -0
- package/src/components/Sidebar/Sidebar.mdx +31 -0
- package/src/components/Sidebar/Sidebar.stories.ts +34 -0
- package/src/components/Sidebar/sidebar.css +18 -0
- package/src/components/Sidebar/sidebar.spec.ts +33 -0
- package/src/components/Snackbar/RSnackbar.vue +136 -0
- package/src/components/Snackbar/Snackbar.mdx +126 -0
- package/src/components/Snackbar/Snackbar.stories.ts +93 -0
- package/src/components/Snackbar/snackbar.css +99 -0
- package/src/components/Snackbar/snackbar.spec.ts +56 -0
- package/src/components/Switch/RSwitch.vue +147 -0
- package/src/components/Switch/Switch.mdx +102 -0
- package/src/components/Switch/Switch.stories.ts +79 -0
- package/src/components/Switch/switch.css +102 -0
- package/src/components/Switch/switch.spec.ts +31 -0
- package/src/components/TabItem/RTabItem.vue +175 -0
- package/src/components/TabItem/TabItem.mdx +95 -0
- package/src/components/TabItem/TabItem.spec.ts +29 -0
- package/src/components/TabItem/TabItem.stories.ts +97 -0
- package/src/components/TabItem/common.ts +6 -0
- package/src/components/TabItem/tab-item.css +29 -0
- package/src/components/Tabs/RTabs.vue +94 -0
- package/src/components/Tabs/Tabs.mdx +78 -0
- package/src/components/Tabs/Tabs.spec.ts +28 -0
- package/src/components/Tabs/Tabs.stories.ts +191 -0
- package/src/components/Tabs/tabs.css +13 -0
- package/src/components/Tabs/types.ts +11 -0
- package/src/components/TextArea/RTextArea.vue +142 -0
- package/src/components/TextArea/TextArea.mdx +108 -0
- package/src/components/TextArea/TextArea.stories.ts +55 -0
- package/src/components/TextArea/textarea.css +51 -0
- package/src/components/TextArea/textarea.spec.ts +36 -0
- package/src/components/Textfield/RTextfield.vue +372 -0
- package/src/components/Textfield/Textfield.mdx +159 -0
- package/src/components/Textfield/Textfield.stories.ts +121 -0
- package/src/components/Textfield/textfield.css +81 -0
- package/src/components/Textfield/textfield.spec.ts +34 -0
- package/src/components/Tooltip/RTooltip.vue +325 -0
- package/src/components/Tooltip/Tooltip.mdx +111 -0
- package/src/components/Tooltip/Tooltip.stories.ts +203 -0
- package/src/components/Tooltip/common.ts +91 -0
- package/src/components/Tooltip/tooltip.css +34 -0
- package/src/components/Tooltip/tooltip.spec.ts +81 -0
- package/src/components/Typography/Typography.mdx +109 -0
- package/src/components/Typography/typography.css +128 -0
- package/src/directives/index.ts +19 -0
- package/src/index.css +241 -0
- package/src/main.ts +5 -0
- package/src/scripts/buildIcons.js +21 -0
- package/src/stories/Colors.mdx +355 -0
- package/src/stories/GettingStarted.mdx +121 -0
- package/src/stories/Layout.mdx +15 -0
- package/tailwind.config.cjs +16 -0
- package/tsconfig.json +24 -0
- package/vite.config.ts +39 -0
- package/vitest.config.ts +12 -0
- package/dist/rocket-ui-vue.js +0 -9381
- package/dist/rocket-ui-vue.umd.cjs +0 -1
- package/dist/style.css +0 -2
- package/dist/types/main.d.ts +0 -25
- /package/{dist → public}/design-tokens.source.json +0 -0
- /package/{dist → public}/favicon.ico +0 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
reactive,
|
|
5
|
+
ref,
|
|
6
|
+
watch,
|
|
7
|
+
type HTMLAttributes,
|
|
8
|
+
type InputHTMLAttributes,
|
|
9
|
+
type LabelHTMLAttributes,
|
|
10
|
+
} from 'vue';
|
|
11
|
+
import './textfield.css';
|
|
12
|
+
import Icon from '../Icon/RIcon.vue';
|
|
13
|
+
import Label from '../Label/RLabel.vue';
|
|
14
|
+
// import { vFocus } from '../../directives';
|
|
15
|
+
export interface Props {
|
|
16
|
+
/**
|
|
17
|
+
* id of the textfield
|
|
18
|
+
* @type HTMLAttributes['id']
|
|
19
|
+
* @default ''
|
|
20
|
+
* @example
|
|
21
|
+
* <Textfield id="textfield" />
|
|
22
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id
|
|
23
|
+
*/
|
|
24
|
+
id: HTMLAttributes['id'];
|
|
25
|
+
/**
|
|
26
|
+
* Input type
|
|
27
|
+
* @type 'text' | 'password' | 'email' | 'number' | 'tel' | 'url'
|
|
28
|
+
* @default 'text'
|
|
29
|
+
* @example
|
|
30
|
+
* <Textfield type="password" />
|
|
31
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
|
|
32
|
+
*/
|
|
33
|
+
type: 'text' | 'password' | 'email' | 'number' | 'tel' | 'url';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Input value
|
|
37
|
+
* @type InputHTMLAttributes['value'];
|
|
38
|
+
* @default ''
|
|
39
|
+
* @example
|
|
40
|
+
* <Textfield modelValue="Hello" />
|
|
41
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#value
|
|
42
|
+
*/
|
|
43
|
+
modelValue?: InputHTMLAttributes['value'];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* label of the textfield
|
|
47
|
+
* @type LabelHTMLAttributes['label']
|
|
48
|
+
* @default ''
|
|
49
|
+
* @example
|
|
50
|
+
* <Textfield label="Textfield" />
|
|
51
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
|
|
52
|
+
*/
|
|
53
|
+
label?: LabelHTMLAttributes['for'];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Placeholder text
|
|
57
|
+
* @type InputHTMLAttributes['placeholder'];
|
|
58
|
+
* @default ''
|
|
59
|
+
* @example
|
|
60
|
+
* <Textfield placeholder="Placeholder" />
|
|
61
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#placeholder
|
|
62
|
+
*/
|
|
63
|
+
placeholder?: InputHTMLAttributes['placeholder'];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Error message
|
|
67
|
+
* @type string
|
|
68
|
+
* @default ''
|
|
69
|
+
* @example
|
|
70
|
+
* <Textfield errorMsg="This is an error" />
|
|
71
|
+
*/
|
|
72
|
+
errorMsg?: string;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Hint text
|
|
76
|
+
* @type string
|
|
77
|
+
* @default ''
|
|
78
|
+
* @example
|
|
79
|
+
* <Textfield hint="This is a hint" />
|
|
80
|
+
*/
|
|
81
|
+
hint?: string;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Icon to prepend
|
|
85
|
+
* @type string
|
|
86
|
+
* @default ''
|
|
87
|
+
* @example
|
|
88
|
+
* <Textfield prependIcon="mdiLock" />
|
|
89
|
+
*/
|
|
90
|
+
prependIcon?: string;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Icon to append
|
|
94
|
+
* @type string
|
|
95
|
+
* @default ''
|
|
96
|
+
* @example
|
|
97
|
+
* <Textfield appendIcon="mdiEyeOffOutline" />
|
|
98
|
+
*/
|
|
99
|
+
appendIcon?: string;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Input disabled state
|
|
103
|
+
* @type InputHTMLAttributes['disabled'] | boolean
|
|
104
|
+
* @default false
|
|
105
|
+
* @example
|
|
106
|
+
* <Textfield disabled="true" />
|
|
107
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#disabled
|
|
108
|
+
*/
|
|
109
|
+
disabled?: boolean;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Input loading state
|
|
113
|
+
* @type boolean
|
|
114
|
+
* @default false
|
|
115
|
+
* @example
|
|
116
|
+
* <Textfield loading="true" />
|
|
117
|
+
*/
|
|
118
|
+
loading?: boolean;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Input clearable state
|
|
122
|
+
* @type boolean
|
|
123
|
+
* @default false
|
|
124
|
+
* @example
|
|
125
|
+
* <Textfield clearable="true" />
|
|
126
|
+
*/
|
|
127
|
+
clearable?: boolean;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Input number min value
|
|
131
|
+
* @type InputHTMLAttributes['min']
|
|
132
|
+
* @default ''
|
|
133
|
+
* @example
|
|
134
|
+
* <Textfield min="0" />
|
|
135
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#min
|
|
136
|
+
*/
|
|
137
|
+
min?: InputHTMLAttributes['min'];
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Input number max value
|
|
141
|
+
* @type InputHTMLAttributes['max']
|
|
142
|
+
* @default ''
|
|
143
|
+
* @example
|
|
144
|
+
* <Textfield max="10" />
|
|
145
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#max
|
|
146
|
+
*/
|
|
147
|
+
max?: InputHTMLAttributes['max'];
|
|
148
|
+
}
|
|
149
|
+
const inputRef = ref<HTMLInputElement>();
|
|
150
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
151
|
+
id: '',
|
|
152
|
+
type: 'text',
|
|
153
|
+
modelValue: '',
|
|
154
|
+
label: '',
|
|
155
|
+
placeholder: '',
|
|
156
|
+
errorMsg: '',
|
|
157
|
+
hint: '',
|
|
158
|
+
prependIcon: '',
|
|
159
|
+
appendIcon: '',
|
|
160
|
+
iconColor: '',
|
|
161
|
+
disabled: false,
|
|
162
|
+
loading: false,
|
|
163
|
+
clearable: false,
|
|
164
|
+
});
|
|
165
|
+
const emit = defineEmits([
|
|
166
|
+
'update:modelValue',
|
|
167
|
+
'focus',
|
|
168
|
+
'blur',
|
|
169
|
+
'input',
|
|
170
|
+
'click:icon',
|
|
171
|
+
'click:clear',
|
|
172
|
+
]);
|
|
173
|
+
const state = reactive({
|
|
174
|
+
value: '',
|
|
175
|
+
});
|
|
176
|
+
const typeOfInputRef = ref(props.type);
|
|
177
|
+
const prependIconsOfType = {
|
|
178
|
+
password: 'mdiLock',
|
|
179
|
+
email: 'mdiEmailOutline',
|
|
180
|
+
tel: 'mdiPhone',
|
|
181
|
+
url: 'mdiLink',
|
|
182
|
+
number: 'mdiNumeric',
|
|
183
|
+
text: '',
|
|
184
|
+
};
|
|
185
|
+
const isFocused = ref(false);
|
|
186
|
+
const isFilled = computed(() => !!state.value);
|
|
187
|
+
const classes = computed(() => {
|
|
188
|
+
const { disabled, loading, clearable, errorMsg } = props;
|
|
189
|
+
return {
|
|
190
|
+
textfield: true,
|
|
191
|
+
'textfield--error': errorMsg?.length,
|
|
192
|
+
'textfield--loading': loading,
|
|
193
|
+
'textfield--disabled': disabled,
|
|
194
|
+
'textfield--clearable': clearable,
|
|
195
|
+
'textfield--focus': isFocused.value,
|
|
196
|
+
'textfield--filled': isFilled.value,
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
const prependIconClasses = computed(() => {
|
|
200
|
+
return {
|
|
201
|
+
'textfield__prepend-icon': true,
|
|
202
|
+
'textfield__prepend-icon--loading': props.loading,
|
|
203
|
+
'textfield__prepend-icon--error': hasErrorMsg.value && isFilled.value,
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
const appendIconClasses = computed(() => {
|
|
207
|
+
return {
|
|
208
|
+
'textfield__append-icon': true,
|
|
209
|
+
'textfield__append-icon--clear': hasClear.value,
|
|
210
|
+
'textfield__append-icon--error': hasErrorMsg.value,
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
const hasValue = computed(() => {
|
|
214
|
+
return state.value.length > 0;
|
|
215
|
+
});
|
|
216
|
+
const hasErrorMsg = computed(() => {
|
|
217
|
+
return props.errorMsg?.length;
|
|
218
|
+
});
|
|
219
|
+
const hasClear = computed(() => {
|
|
220
|
+
return props.clearable && hasValue.value;
|
|
221
|
+
});
|
|
222
|
+
const prependIconName = computed(() => {
|
|
223
|
+
const { prependIcon, type } = props;
|
|
224
|
+
if (prependIcon === 'none') return '';
|
|
225
|
+
if (prependIcon) return prependIcon;
|
|
226
|
+
return prependIconsOfType[type];
|
|
227
|
+
});
|
|
228
|
+
const appendIconName = computed(() => {
|
|
229
|
+
const { appendIcon, type } = props;
|
|
230
|
+
if (appendIcon === 'none') return '';
|
|
231
|
+
if (hasErrorMsg.value) return 'mdiAlertCircleOutline';
|
|
232
|
+
if (hasClear.value && ['text', 'email'].includes(type)) return 'mdiClose';
|
|
233
|
+
if (type === 'password' && typeOfInputRef.value === 'password')
|
|
234
|
+
return 'mdiEyeOutline';
|
|
235
|
+
if (type === 'password' && typeOfInputRef.value === 'text')
|
|
236
|
+
return 'mdiEyeOffOutline';
|
|
237
|
+
return appendIcon;
|
|
238
|
+
});
|
|
239
|
+
/**
|
|
240
|
+
* @description - focus event handler
|
|
241
|
+
* @param {FocusEvent} e - FocusEvent object
|
|
242
|
+
* @returns {void}
|
|
243
|
+
*/
|
|
244
|
+
const onFocus = (e: FocusEvent) => {
|
|
245
|
+
isFocused.value = true;
|
|
246
|
+
emit('focus', e);
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* @description - blur event handler
|
|
250
|
+
* @param {FocusEvent} e - FocusEvent object
|
|
251
|
+
* @returns {void}
|
|
252
|
+
*/
|
|
253
|
+
const onBlur = (e: FocusEvent) => {
|
|
254
|
+
isFocused.value = false;
|
|
255
|
+
emit('blur', e);
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* @description - Emit input event with value of input
|
|
259
|
+
* @param {InputEvent} e - InputEvent object
|
|
260
|
+
* @returns {void}
|
|
261
|
+
*/
|
|
262
|
+
const onInput = (e: InputEvent): void => {
|
|
263
|
+
state.value = (e.target as HTMLInputElement).value;
|
|
264
|
+
emit('input', e);
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* @description - Emit click event with value of append icon
|
|
268
|
+
* @returns {void}
|
|
269
|
+
*/
|
|
270
|
+
const clickIcon = (): void => {
|
|
271
|
+
if (hasClear.value) {
|
|
272
|
+
state.value = '';
|
|
273
|
+
inputRef.value?.focus();
|
|
274
|
+
emit('click:clear', {
|
|
275
|
+
value: typeOfInputRef.value === 'number' ? +state.value : state.value,
|
|
276
|
+
});
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
emit('click:icon', {
|
|
280
|
+
value: typeOfInputRef.value === 'number' ? +state.value : state.value,
|
|
281
|
+
});
|
|
282
|
+
if (typeOfInputRef.value === 'password') setPassType();
|
|
283
|
+
};
|
|
284
|
+
/**
|
|
285
|
+
* @description - Set type of input to password or text
|
|
286
|
+
* @returns {void}
|
|
287
|
+
*/
|
|
288
|
+
const setPassType = (): void => {
|
|
289
|
+
typeOfInputRef.value =
|
|
290
|
+
typeOfInputRef.value === 'password' ? 'text' : 'password';
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const focusInput = () => {
|
|
294
|
+
inputRef.value?.focus();
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
watch(
|
|
298
|
+
() => props.modelValue,
|
|
299
|
+
(value) => {
|
|
300
|
+
state.value = value;
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
immediate: true,
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
watch(
|
|
307
|
+
() => state.value,
|
|
308
|
+
(value) => {
|
|
309
|
+
emit(
|
|
310
|
+
'update:modelValue',
|
|
311
|
+
typeOfInputRef.value === 'number' ? +value : value
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
</script>
|
|
316
|
+
<template>
|
|
317
|
+
<fieldset>
|
|
318
|
+
<div
|
|
319
|
+
:class="{
|
|
320
|
+
textfield__wrapper: true,
|
|
321
|
+
}"
|
|
322
|
+
>
|
|
323
|
+
<Label
|
|
324
|
+
:id="props.id"
|
|
325
|
+
:class="{
|
|
326
|
+
textfield__label: true,
|
|
327
|
+
}"
|
|
328
|
+
:for="props.id"
|
|
329
|
+
:text="props.label"
|
|
330
|
+
@click="focusInput"
|
|
331
|
+
/>
|
|
332
|
+
<div class="input-wrapper">
|
|
333
|
+
<div :class="classes">
|
|
334
|
+
<slot name="prepend" />
|
|
335
|
+
<Icon
|
|
336
|
+
v-if="prependIconName && !$slots['prepend']"
|
|
337
|
+
:class="prependIconClasses"
|
|
338
|
+
:name="prependIconName"
|
|
339
|
+
:size="20"
|
|
340
|
+
/>
|
|
341
|
+
<input
|
|
342
|
+
ref="inputRef"
|
|
343
|
+
:id="props.id"
|
|
344
|
+
:disabled="props.disabled"
|
|
345
|
+
:placeholder="props.placeholder"
|
|
346
|
+
:type="typeOfInputRef"
|
|
347
|
+
:value="state.value"
|
|
348
|
+
:min="props.min"
|
|
349
|
+
:max="props.max"
|
|
350
|
+
@blur="onBlur"
|
|
351
|
+
@focus="onFocus"
|
|
352
|
+
@input="onInput"
|
|
353
|
+
/>
|
|
354
|
+
<slot name="append" />
|
|
355
|
+
<Icon
|
|
356
|
+
v-if="appendIconName && !$slots['append']"
|
|
357
|
+
:class="appendIconClasses"
|
|
358
|
+
:name="`${appendIconName}`"
|
|
359
|
+
:size="20"
|
|
360
|
+
@click="clickIcon"
|
|
361
|
+
/>
|
|
362
|
+
</div>
|
|
363
|
+
<div v-if="props.errorMsg" class="textfield__error">
|
|
364
|
+
{{ props.errorMsg }}
|
|
365
|
+
</div>
|
|
366
|
+
<div v-if="!props.errorMsg && props.hint" class="textfield__hint">
|
|
367
|
+
{{ props.hint }}
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
</fieldset>
|
|
372
|
+
</template>
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Canvas, Meta, Story, Controls } from '@storybook/blocks';
|
|
2
|
+
import * as TextfieldStories from './Textfield.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={TextfieldStories} />
|
|
5
|
+
|
|
6
|
+
# Textfield
|
|
7
|
+
|
|
8
|
+
The Textfield component is a versatile input field that can be used for various types of data entry in a web application.
|
|
9
|
+
|
|
10
|
+
- [Overview](#overview)
|
|
11
|
+
|
|
12
|
+
- [Playground](#playground)
|
|
13
|
+
|
|
14
|
+
- [Usage with props](#usage)
|
|
15
|
+
|
|
16
|
+
- [Stories](#stories)
|
|
17
|
+
|
|
18
|
+
- [Tips](#tips)
|
|
19
|
+
|
|
20
|
+
## Overview <a id="overview" />
|
|
21
|
+
|
|
22
|
+
The Textfield component allows for the input of text, numbers, and other types of data. It includes various options for customization, such as label, placeholder, and icon. It also includes options for error handling, hinting, and disabling or loading the field.
|
|
23
|
+
|
|
24
|
+
<Canvas>
|
|
25
|
+
<Story of={TextfieldStories.Overview} />
|
|
26
|
+
</Canvas>
|
|
27
|
+
|
|
28
|
+
### Playground <a id="playground" />
|
|
29
|
+
|
|
30
|
+
> Changes you make in the controls will be reflected in the example above.
|
|
31
|
+
|
|
32
|
+
<Controls
|
|
33
|
+
of={TextfieldStories.Overview}
|
|
34
|
+
exclude={/^(change|input|focus|blur|click|on.*)/}
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
## Stories
|
|
38
|
+
|
|
39
|
+
### Text
|
|
40
|
+
|
|
41
|
+
<Canvas>
|
|
42
|
+
<Story of={TextfieldStories.Text} />
|
|
43
|
+
</Canvas>
|
|
44
|
+
|
|
45
|
+
### Mail
|
|
46
|
+
|
|
47
|
+
<Canvas>
|
|
48
|
+
<Story of={TextfieldStories.Mail} />
|
|
49
|
+
</Canvas>
|
|
50
|
+
|
|
51
|
+
### Password
|
|
52
|
+
|
|
53
|
+
<Canvas>
|
|
54
|
+
<Story of={TextfieldStories.Password} />
|
|
55
|
+
</Canvas>
|
|
56
|
+
|
|
57
|
+
### Url
|
|
58
|
+
|
|
59
|
+
<Canvas>
|
|
60
|
+
<Story of={TextfieldStories.Url} />
|
|
61
|
+
</Canvas>
|
|
62
|
+
|
|
63
|
+
### Tel
|
|
64
|
+
|
|
65
|
+
<Canvas>
|
|
66
|
+
<Story of={TextfieldStories.Tel} />
|
|
67
|
+
</Canvas>
|
|
68
|
+
|
|
69
|
+
### Number
|
|
70
|
+
|
|
71
|
+
<Canvas>
|
|
72
|
+
<Story of={TextfieldStories.Number} />
|
|
73
|
+
</Canvas>
|
|
74
|
+
|
|
75
|
+
### Error
|
|
76
|
+
|
|
77
|
+
<Canvas>
|
|
78
|
+
<Story of={TextfieldStories.Error} />
|
|
79
|
+
</Canvas>
|
|
80
|
+
|
|
81
|
+
## Usage with props <a id="usage" />
|
|
82
|
+
|
|
83
|
+
### id (required)
|
|
84
|
+
|
|
85
|
+
The **id** prop is a unique identifier for the input field.
|
|
86
|
+
|
|
87
|
+
### label (optional)
|
|
88
|
+
|
|
89
|
+
The **label** prop is for text to be displayed as a label for the input field.
|
|
90
|
+
|
|
91
|
+
### placeholder (optional)
|
|
92
|
+
|
|
93
|
+
The **placeholder** prop is for text to be displayed as a placeholder in the input field.
|
|
94
|
+
|
|
95
|
+
### type (required)
|
|
96
|
+
|
|
97
|
+
The **type** is the type of input field, such as `text` or `number`.
|
|
98
|
+
|
|
99
|
+
### modelValue (optional)
|
|
100
|
+
|
|
101
|
+
The **modelValue** prop is the current value of the input field.
|
|
102
|
+
|
|
103
|
+
### errorMsg (optional)
|
|
104
|
+
|
|
105
|
+
The **errorMsg** props is for the error message to be displayed if there is an error with the input.
|
|
106
|
+
|
|
107
|
+
### hint (optional)
|
|
108
|
+
|
|
109
|
+
The **hint** prop is the additional text to provide context or instructions for the input field.
|
|
110
|
+
|
|
111
|
+
### prependIcon (optional)
|
|
112
|
+
|
|
113
|
+
The **prependIcon** prop is an icon to be displayed before the input field.
|
|
114
|
+
|
|
115
|
+
The **prependIcon** prop displays an icon based on the type of the input.
|
|
116
|
+
If you don't want to display an icon in this case, you can pass `none` as the value.
|
|
117
|
+
|
|
118
|
+
### appendIcon (optional)
|
|
119
|
+
|
|
120
|
+
The **appendIcon** prop is an icon to be displayed after the input field.
|
|
121
|
+
|
|
122
|
+
The **appendIcon** prop displays an icon based on the type of the input.
|
|
123
|
+
If you don't want to display an icon in this case, you can pass `none` as the value.
|
|
124
|
+
|
|
125
|
+
### iconColor (optional)
|
|
126
|
+
|
|
127
|
+
The **iconColor** prop gives the color of the icon.
|
|
128
|
+
|
|
129
|
+
### disabled (optional)
|
|
130
|
+
|
|
131
|
+
The **disabled** prop indicates whether the input field is disabled or not.
|
|
132
|
+
|
|
133
|
+
### loading (optional)
|
|
134
|
+
|
|
135
|
+
The **loading** prop indicates whether the input field is in a loading state or not.
|
|
136
|
+
|
|
137
|
+
### clearable (optional)
|
|
138
|
+
|
|
139
|
+
The **clearable** prop indicates whether the input field can be cleared or not.
|
|
140
|
+
|
|
141
|
+
## Tips <a id="tips" />
|
|
142
|
+
|
|
143
|
+
💡 The label should clearly describe what the input field is for, and should be positioned in a way that is easy for users to understand.
|
|
144
|
+
|
|
145
|
+
💡 Placeholders can be used to provide additional context or instructions for the input field, but should not be used as a substitute for a clear label.
|
|
146
|
+
|
|
147
|
+
💡 The type of input field should match the type of data that will be entered. For example, use a number input field for numeric data, and a password input field for sensitive information.
|
|
148
|
+
|
|
149
|
+
💡 Error messages should clearly indicate what the problem is and how to fix it.
|
|
150
|
+
|
|
151
|
+
💡 Icons can be used to make the Textfield component more visually appealing and to provide additional information or context.
|
|
152
|
+
|
|
153
|
+
💡 Textfield component should be accessible to users with disabilities, including keyboard navigation, and providing appropriate ARIA attributes.
|
|
154
|
+
|
|
155
|
+
💡 Hints can be used to provide additional context or instructions for the input field.
|
|
156
|
+
|
|
157
|
+
💡 This will help users to clear their input easily.
|
|
158
|
+
|
|
159
|
+
💡 Use these states to indicate that the input field is temporarily unavailable or in the process of loading data.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
2
|
+
import Textfield from './RTextfield.vue';
|
|
3
|
+
|
|
4
|
+
const DefaultArgTypes = {
|
|
5
|
+
type: {
|
|
6
|
+
type: 'select',
|
|
7
|
+
options: ['text', 'password', 'email', 'number', 'tel', 'url'],
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DefaultArgs = {
|
|
12
|
+
id: 'textfield',
|
|
13
|
+
type: 'text',
|
|
14
|
+
label: 'Textfield',
|
|
15
|
+
placeholder: 'Placeholder',
|
|
16
|
+
modelValue: '',
|
|
17
|
+
errorMsg: '',
|
|
18
|
+
hint: 'This is a hint text to help user.',
|
|
19
|
+
prependIcon: '',
|
|
20
|
+
appendIcon: '',
|
|
21
|
+
loading: false,
|
|
22
|
+
disabled: false,
|
|
23
|
+
clearable: false,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const TextfieldStory = {
|
|
27
|
+
title: 'Form/Textfield',
|
|
28
|
+
component: Textfield,
|
|
29
|
+
setup(args: typeof Textfield) {
|
|
30
|
+
return {
|
|
31
|
+
args,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
template: `<Textfield v-bind="args" />`,
|
|
35
|
+
argTypes: {
|
|
36
|
+
...DefaultArgTypes,
|
|
37
|
+
onInput: { action: 'input' },
|
|
38
|
+
onFocus: { action: 'focus' },
|
|
39
|
+
onBlur: { action: 'blur' },
|
|
40
|
+
'onClick:icon': { action: 'click:icon' },
|
|
41
|
+
'onClick:clear': { action: 'click:clear' },
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
args: {},
|
|
45
|
+
} as Meta<typeof Textfield>;
|
|
46
|
+
|
|
47
|
+
export default TextfieldStory;
|
|
48
|
+
|
|
49
|
+
type Story = StoryObj<typeof TextfieldStory>;
|
|
50
|
+
|
|
51
|
+
export const Overview: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
id: 'textfield-id',
|
|
54
|
+
label: 'Textfield label',
|
|
55
|
+
placeholder: 'Textfield placeholder',
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const Text: Story = {
|
|
60
|
+
args: {
|
|
61
|
+
type: 'text',
|
|
62
|
+
id: 'textfield-id',
|
|
63
|
+
label: 'Textfield label',
|
|
64
|
+
placeholder: 'Textfield placeholder',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Mail: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
type: 'email',
|
|
71
|
+
id: 'textfield-id',
|
|
72
|
+
label: 'Textfield label',
|
|
73
|
+
placeholder: 'Textfield placeholder',
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const Password: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
type: 'password',
|
|
80
|
+
id: 'textfield-id',
|
|
81
|
+
label: 'Textfield label',
|
|
82
|
+
placeholder: 'Textfield placeholder',
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const Url: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
type: 'url',
|
|
89
|
+
id: 'textfield-id',
|
|
90
|
+
label: 'Textfield label',
|
|
91
|
+
placeholder: 'Textfield placeholder',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const Tel: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
type: 'tel',
|
|
98
|
+
id: 'textfield-id',
|
|
99
|
+
label: 'Textfield label',
|
|
100
|
+
placeholder: 'Textfield placeholder',
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const Number: Story = {
|
|
105
|
+
args: {
|
|
106
|
+
type: 'number',
|
|
107
|
+
id: 'textfield-id',
|
|
108
|
+
label: 'Textfield label',
|
|
109
|
+
placeholder: 'Textfield placeholder',
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const Error: Story = {
|
|
114
|
+
args: {
|
|
115
|
+
type: 'text',
|
|
116
|
+
id: 'textfield-id',
|
|
117
|
+
label: 'Textfield label',
|
|
118
|
+
placeholder: 'Textfield placeholder',
|
|
119
|
+
errorMsg: 'This is an error message',
|
|
120
|
+
},
|
|
121
|
+
};
|