pns-component-library 1.5.14 → 1.6.1
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/README.md +531 -87
- package/dist/pns-component-library.es.js +2268 -663
- package/dist/pns-component-library.umd.js +2 -2
- package/package.json +1 -1
- package/src/composables/useWindowSize.js +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A comprehensive Vue 3 component library built with modern development practices,
|
|
|
4
4
|
|
|
5
5
|
## 🚀 Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
7
|
+
- **11 Production-Ready Components** - Form controls, layout components, and utility components
|
|
8
8
|
- **2 Powerful Composables** - Reusable logic for common patterns
|
|
9
9
|
- **2 Custom Directives** - Loading states and UI enhancements
|
|
10
10
|
- **Vue 3 Composition API** - Modern, performant, and type-safe
|
|
@@ -12,11 +12,32 @@ A comprehensive Vue 3 component library built with modern development practices,
|
|
|
12
12
|
- **Accessibility** - ARIA attributes and keyboard navigation support
|
|
13
13
|
- **Customizable Themes** - Multiple built-in color schemes
|
|
14
14
|
|
|
15
|
+
> Newly released public components: `DropdownMenu`, `Badge`, `FloatingActionButton`, `DynamicColorResponsiveButton`.
|
|
16
|
+
|
|
17
|
+
## 🆕 What's New
|
|
18
|
+
|
|
19
|
+
### 2025-10-28
|
|
20
|
+
|
|
21
|
+
- Public release of:
|
|
22
|
+
- `DropdownMenu`
|
|
23
|
+
- `Badge`
|
|
24
|
+
- `FloatingActionButton`
|
|
25
|
+
- `DynamicColorResponsiveButton`
|
|
26
|
+
|
|
27
|
+
Import options:
|
|
28
|
+
|
|
29
|
+
- Global (plugin):
|
|
30
|
+
- `app.use(PnsComponentLibrary)` then use components directly in templates.
|
|
31
|
+
- Named import:
|
|
32
|
+
- `import { DropdownMenu, Badge, FloatingActionButton, DynamicColorResponsiveButton } from 'pns-component-library'`
|
|
33
|
+
|
|
15
34
|
## 📦 Installation
|
|
16
35
|
|
|
17
36
|
```bash
|
|
18
37
|
npm install pns-component-library
|
|
19
38
|
```
|
|
39
|
+
|
|
40
|
+
|
|
20
41
|
or
|
|
21
42
|
|
|
22
43
|
```bash
|
|
@@ -52,7 +73,15 @@ app.mount('#app')
|
|
|
52
73
|
<!-- Phone input with country selection -->
|
|
53
74
|
<AreaCodePhoneInput v-model="phoneData" />
|
|
54
75
|
|
|
55
|
-
<!--
|
|
76
|
+
<!-- Dynamic color responsive button (Recommended) -->
|
|
77
|
+
<DynamicColorResponsiveButton
|
|
78
|
+
display_name="Click me"
|
|
79
|
+
button_type="filled"
|
|
80
|
+
built_in_theme="primary"
|
|
81
|
+
@click-button="handleClick"
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
<!-- Legacy button (Deprecated but still works; not maintained). New projects should migrate to DynamicColorResponsiveButton. -->
|
|
56
85
|
<ResponsiveButton @click="handleClick">
|
|
57
86
|
Click me
|
|
58
87
|
</ResponsiveButton>
|
|
@@ -118,104 +147,197 @@ International phone number input with country selection and area code handling.
|
|
|
118
147
|
</template>
|
|
119
148
|
```
|
|
120
149
|
|
|
150
|
+
### Deprecated Components
|
|
151
|
+
|
|
152
|
+
> Note: Deprecated components still work and are supported for backward compatibility, but they are no longer maintained. For new development, please migrate to the recommended replacements.
|
|
153
|
+
|
|
154
|
+
#### ResponsiveButton (Deprecated, still works)
|
|
155
|
+
Legacy feature-rich button component superseded by `DynamicColorResponsiveButton`.
|
|
156
|
+
|
|
157
|
+
##### Attributes
|
|
158
|
+
|
|
159
|
+
| Attribute | Description | Type | Default |
|
|
160
|
+
|-----------|-------------|------|---------|
|
|
161
|
+
| display_name | button text | `string` | `'button'` |
|
|
162
|
+
| size | button size | `'xsmall' \| 'small' \| 'medium' \| 'large'` | `'small'` |
|
|
163
|
+
| width_type | button width type | `'fill-whole' \| 'fit-content'` | `'fit-content'` |
|
|
164
|
+
| build_in_theme | color theme | `'outlined-primary' \| 'filled-primary' \| 'text-primary' \| 'outlined-secondary' \| 'filled-secondary' \| 'text-secondary'` | `'outlined-primary'` |
|
|
165
|
+
| customized_class | custom CSS class | `string` | — |
|
|
166
|
+
| hold | whether button is in hold state | `boolean` | `false` |
|
|
167
|
+
| disabled | whether button is disabled | `boolean` | `false` |
|
|
168
|
+
| dropdown_options | dropdown menu options | `array` | `[]` |
|
|
169
|
+
| is_dropdown_option | whether this is a dropdown item | `boolean` | `false` |
|
|
170
|
+
|
|
171
|
+
##### Events
|
|
172
|
+
|
|
173
|
+
| Event | Description | Parameters |
|
|
174
|
+
|-------|-------------|------------|
|
|
175
|
+
| click | triggers when button is clicked | `(event: MouseEvent)` |
|
|
176
|
+
| update-currently-hovered-dropdown-option | triggers when dropdown option is hovered | `(option: object)` |
|
|
177
|
+
| add-nested-dropdown-history-stack | triggers when nested dropdown is navigated | `(option: object)` |
|
|
178
|
+
| remove-nested-dropdown-history-stack | triggers when navigating back in nested dropdown | — |
|
|
179
|
+
|
|
180
|
+
##### Slots
|
|
181
|
+
|
|
182
|
+
| Name | Description |
|
|
183
|
+
|------|-------------|
|
|
184
|
+
| prefix | content before button text |
|
|
185
|
+
| suffix | content after button text |
|
|
186
|
+
|
|
187
|
+
```vue
|
|
188
|
+
<template>
|
|
189
|
+
<ResponsiveButton
|
|
190
|
+
display_name="Save Changes"
|
|
191
|
+
size="medium"
|
|
192
|
+
build_in_theme="filled-primary"
|
|
193
|
+
@click="handleSave"
|
|
194
|
+
>
|
|
195
|
+
<template #prefix>
|
|
196
|
+
<img :src="iconsMap['eye_icon']" alt="eye" />
|
|
197
|
+
</template>
|
|
198
|
+
</ResponsiveButton>
|
|
199
|
+
</template>
|
|
200
|
+
```
|
|
201
|
+
|
|
121
202
|
#### InputBox
|
|
122
|
-
Versatile input field
|
|
203
|
+
Versatile input field with clearable icon, inside/outside label, styled themes, and validation helpers. The component dynamically adjusts border/background/label colors based on state (hover, focus, disabled, alert, error) and supports prefix/suffix slots.
|
|
123
204
|
|
|
124
205
|
##### Attributes
|
|
125
206
|
|
|
126
207
|
| Attribute | Description | Type | Default |
|
|
127
208
|
|-----------|-------------|------|---------|
|
|
128
|
-
| v-model / modelValue |
|
|
129
|
-
|
|
|
209
|
+
| v-model / modelValue | bound input value | `string` | `''` |
|
|
210
|
+
| label | label text (optional) | `string` | `''` |
|
|
211
|
+
| label_type | where to render the label | `'inside' \| 'outside'` | `'outside'` |
|
|
212
|
+
| theme_type | visual styling theme | `'filled' \| 'outlined'` | `'outlined'` |
|
|
213
|
+
| customized_theme_color | focus/brand color used for focus border or underline; any valid CSS color or CSS var | `string` | `''` |
|
|
214
|
+
| type | native input type | `string` | `'text'` |
|
|
130
215
|
| placeholder | placeholder text | `string` | `'Enter'` |
|
|
131
|
-
| clearable |
|
|
132
|
-
|
|
|
133
|
-
|
|
|
216
|
+
| clearable | show a clear icon when focused/hovered and value is non-empty | `boolean` | `false` |
|
|
217
|
+
| required | show a required asterisk on the label. Visual-only; does not enforce validation | `boolean` | `false` |
|
|
218
|
+
| disabled | disable the input | `boolean` | `false` |
|
|
219
|
+
| autocomplete | native `autocomplete` | `string` | `'off'` |
|
|
220
|
+
|
|
221
|
+
Behavior notes:
|
|
222
|
+
|
|
223
|
+
- In `filled` theme, a bottom inset shadow emulates the underline. In `outlined` theme, an inset border is used. Colors are derived from:
|
|
224
|
+
- Focus: `customized_theme_color` (or `var(--Schemes-primary)` fallback)
|
|
225
|
+
- Hover: `#17181C`
|
|
226
|
+
- Disabled: `var(--disabled-color--)` and `var(--disabled-button-background-color--)`
|
|
227
|
+
- Alert/Error: `var(--semantic-alert-color--)` / `var(--semantic-error-color--)`
|
|
228
|
+
- Inside label floats above the input content area and inherits a state color similar to the border color.
|
|
229
|
+
- A message region appears below the field when either `#supportingText` slot has content or when alert/error is active with a message.
|
|
230
|
+
- `#supportingText` is neutral and does not adopt alert/error colors; alert/error messages are colored separately.
|
|
231
|
+
- Alert/Error messages are set via exposes: `alert(message)` / `error(message)`; call `removeAlertOrErrorEffect()` to clear.
|
|
232
|
+
- Disabled state down-weights message colors.
|
|
134
233
|
|
|
135
234
|
##### Events
|
|
136
235
|
|
|
137
236
|
| Event | Description | Parameters |
|
|
138
237
|
|-------|-------------|------------|
|
|
139
|
-
| input |
|
|
140
|
-
| change |
|
|
141
|
-
| focus |
|
|
142
|
-
| blur |
|
|
143
|
-
| clear |
|
|
238
|
+
| input | fires on each keystroke | `(value: string)` |
|
|
239
|
+
| change | fires on Enter or blur | `(value: string)` |
|
|
240
|
+
| focus | input focused | `()` |
|
|
241
|
+
| blur | input blurred | `()` |
|
|
242
|
+
| clear | clear button clicked | `()` |
|
|
144
243
|
|
|
145
244
|
##### Exposes
|
|
146
245
|
|
|
147
|
-
| Method | Description |
|
|
148
|
-
|
|
149
|
-
| focus | focus the input |
|
|
150
|
-
| blur | blur the input |
|
|
151
|
-
| alert | show alert
|
|
152
|
-
| error | show error
|
|
153
|
-
| removeAlertOrErrorEffect | clear alert/error state |
|
|
246
|
+
| Method | Description |
|
|
247
|
+
|--------|-------------|
|
|
248
|
+
| focus | programmatically focus the input |
|
|
249
|
+
| blur | programmatically blur the input |
|
|
250
|
+
| alert | show alert state with a message (yellow) |
|
|
251
|
+
| error | show error state with a message (red) |
|
|
252
|
+
| removeAlertOrErrorEffect | clear alert/error state |
|
|
154
253
|
|
|
155
254
|
##### Slots
|
|
156
255
|
|
|
157
256
|
| Name | Description |
|
|
158
257
|
|------|-------------|
|
|
159
|
-
| prefix | content before input |
|
|
160
|
-
| suffix | content after input |
|
|
258
|
+
| prefix | content rendered before the input (e.g., an icon) |
|
|
259
|
+
| suffix | content rendered after the input (e.g., an icon) |
|
|
260
|
+
| supportingText | optional helper text rendered below the field (e.g., character counter like `0/100`). This is distinct from alert/error messages and uses a neutral color. When alert/error is active, their messages render alongside (with their own colors) |
|
|
261
|
+
|
|
262
|
+
> Tip: For prefix/suffix icons, prefer inline SVG that uses `fill="currentColor"`. This way the icon color automatically follows the component state (hover/focus/disabled/alert/error). If you use `<img>` sources, they won't inherit color.
|
|
263
|
+
|
|
264
|
+
##### Usage
|
|
161
265
|
|
|
162
266
|
```vue
|
|
163
|
-
<
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
267
|
+
<template>
|
|
268
|
+
<InputBox
|
|
269
|
+
v-model="value"
|
|
270
|
+
label="Email"
|
|
271
|
+
label_type="inside"
|
|
272
|
+
theme_type="outlined"
|
|
273
|
+
customized_theme_color="var(--Schemes-primary)"
|
|
274
|
+
type="email"
|
|
275
|
+
placeholder="name@example.com"
|
|
276
|
+
clearable
|
|
277
|
+
autocomplete="email"
|
|
278
|
+
>
|
|
279
|
+
<template #prefix>
|
|
280
|
+
<img src="/icons/mail.svg" style="width:100%;height:100%"/>
|
|
281
|
+
</template>
|
|
282
|
+
</InputBox>
|
|
283
|
+
</template>
|
|
284
|
+
|
|
285
|
+
<script setup>
|
|
286
|
+
import { ref } from 'vue'
|
|
287
|
+
const value = ref('')
|
|
288
|
+
</script>
|
|
174
289
|
|
|
175
290
|
#### SingleSelector
|
|
176
|
-
Dropdown selector
|
|
291
|
+
Dropdown selector with inside/outside label, themed styling, filtering, and optional remote search. Mirrors `InputBox` visual behavior (hover/focus/disabled/alert/error), and supports prefix/suffix slots plus a neutral `supportingText` message region.
|
|
177
292
|
|
|
178
293
|
##### Attributes
|
|
179
294
|
|
|
180
295
|
| Attribute | Description | Type | Default |
|
|
181
296
|
|-----------|-------------|------|---------|
|
|
182
|
-
| v-model / modelValue | binding value
|
|
183
|
-
|
|
|
184
|
-
|
|
|
185
|
-
|
|
|
186
|
-
|
|
|
187
|
-
|
|
|
188
|
-
|
|
|
189
|
-
|
|
|
297
|
+
| v-model / modelValue | binding value (selected option's `value`) | `string \| number \| boolean \| object \| array` |
|
|
298
|
+
| label | label text | `string` | `''` |
|
|
299
|
+
| label_type | label position | `'inside' \| 'outside'` | `'outside'` |
|
|
300
|
+
| theme_type | visual theme | `'filled' \| 'outlined'` | `'outlined'` |
|
|
301
|
+
| customized_theme_color | focus/brand color; any CSS color or CSS var | `string` | `''` |
|
|
302
|
+
| required | show red asterisk next to label | `boolean` | `false` |
|
|
303
|
+
| options | options array | `Array<{ value: string \| number \| boolean \| object, label: string, prefix?: string, suffix?: string, prefix_slot_raw_html_content?: string, suffix_slot_raw_html_content?: string, options?: Option[], has_divider?: boolean, disabled?: boolean, is_selected?: boolean }>` | `[]` |
|
|
304
|
+
| filterable | enable client-side filter input | `boolean` | `false` |
|
|
305
|
+
| remote_search | emit query outward instead of local filtering | `boolean` | `false` |
|
|
306
|
+
| disabled | disable selector | `boolean` | `false` |
|
|
307
|
+
| placeholder | placeholder when no value | `string` | `'Select'` |
|
|
308
|
+
| clearable | show clear icon when there is a value and focused/hovered | `boolean` | `false` |
|
|
309
|
+
| loading | loading state (mirrors dropdown's `is_loading`) | `boolean` | `false` |
|
|
190
310
|
| loading_text | loading text | `string` | `'Loading'` |
|
|
191
|
-
| no_data_text |
|
|
192
|
-
| filter_only_among_options_value | filter
|
|
193
|
-
| filter_only_among_options_label | filter
|
|
311
|
+
| no_data_text | empty-state text | `string` | `'No data'` |
|
|
312
|
+
| filter_only_among_options_value | filter against serialized `value` only | `boolean` | `false` |
|
|
313
|
+
| filter_only_among_options_label | filter against `label` only | `boolean` | `false` |
|
|
314
|
+
|
|
315
|
+
Notes:
|
|
194
316
|
|
|
195
|
-
|
|
317
|
+
- `filter_only_among_options_value` and `filter_only_among_options_label` cannot both be `true`. If both are `true`, filtering uses `value`.
|
|
318
|
+
- Option `value` may be primitive or object; selection compares by `value` plus `label` for marking `is_selected` in lists.
|
|
196
319
|
|
|
197
320
|
##### Events
|
|
198
321
|
|
|
199
322
|
| Event | Description | Parameters |
|
|
200
323
|
|-------|-------------|------------|
|
|
201
324
|
| change | triggers when the binding value changes | `(value: any)` |
|
|
202
|
-
| remote-search | triggers when
|
|
203
|
-
| filter-change | triggers
|
|
204
|
-
| visible-change |
|
|
205
|
-
| focus |
|
|
206
|
-
| blur |
|
|
207
|
-
| clear |
|
|
325
|
+
| remote-search | triggers when `remote_search=true` and user types | `(query: string)` |
|
|
326
|
+
| filter-change | triggers whenever filter text changes | `(query: string)` |
|
|
327
|
+
| visible-change | dropdown visibility changes | `(visible: boolean)` |
|
|
328
|
+
| focus | selector focused | `()` |
|
|
329
|
+
| blur | selector blurred | `()` |
|
|
330
|
+
| clear | clear button clicked | `()` |
|
|
208
331
|
|
|
209
332
|
##### Exposes
|
|
210
333
|
|
|
211
|
-
| Method | Description |
|
|
212
|
-
|
|
213
|
-
| focus | focus the selector |
|
|
214
|
-
| blur | blur the selector |
|
|
215
|
-
| alert | show alert
|
|
216
|
-
| error | show error
|
|
217
|
-
| removeAlertOrErrorEffect | clear alert/error state |
|
|
218
|
-
| setDropdownContentLoading | set loading state | `(loading: boolean) => void` |
|
|
334
|
+
| Method | Description |
|
|
335
|
+
|--------|-------------|
|
|
336
|
+
| focus | programmatically focus the selector |
|
|
337
|
+
| blur | programmatically blur the selector |
|
|
338
|
+
| alert | show alert state with a message (yellow) |
|
|
339
|
+
| error | show error state with a message (red) |
|
|
340
|
+
| removeAlertOrErrorEffect | clear alert/error state |
|
|
219
341
|
|
|
220
342
|
##### Slots
|
|
221
343
|
|
|
@@ -223,11 +345,13 @@ Dropdown selector component with filtering and search capabilities.
|
|
|
223
345
|
|------|-------------|
|
|
224
346
|
| prefix | content before selector |
|
|
225
347
|
| suffix | content after selector |
|
|
348
|
+
| supportingText | neutral helper text under the field; alert/error messages render alongside in their own colors |
|
|
226
349
|
|
|
227
350
|
```vue
|
|
228
351
|
<SingleSelector
|
|
229
352
|
v-model="selected"
|
|
230
353
|
:options="options"
|
|
354
|
+
{{ ... }}
|
|
231
355
|
filterable
|
|
232
356
|
clearable
|
|
233
357
|
placeholder="Choose option"
|
|
@@ -235,9 +359,25 @@ Dropdown selector component with filtering and search capabilities.
|
|
|
235
359
|
<template #prefix>
|
|
236
360
|
<Icon name="search" />
|
|
237
361
|
</template>
|
|
362
|
+
<template #supportingText>
|
|
363
|
+
<!-- e.g. instruction or character counter; neutral color -->
|
|
364
|
+
Pick one option
|
|
365
|
+
</template>
|
|
238
366
|
</SingleSelector>
|
|
239
367
|
```
|
|
240
368
|
|
|
369
|
+
##### DropdownMenu (dev-only)
|
|
370
|
+
|
|
371
|
+
Used internally by components to render options lists.
|
|
372
|
+
|
|
373
|
+
- Props: `width_type: 'fill-whole'|'fit-content'`, `options` (same schema as above), `size: 'small'|'medium'|'large'`, `with_box_shadow`, `is_loading`, `loading_text`, `no_data_text`.
|
|
374
|
+
- Events:
|
|
375
|
+
- `select-dropdown-option(value, label, in_dropdown_level)`
|
|
376
|
+
- `add-new-shown-nested-dropdown(target_item_props, trigger)`
|
|
377
|
+
- `remove-shown-nested-dropdown(target_item_props)`
|
|
378
|
+
- `update-shown-nested-dropdown(target_item_props)`
|
|
379
|
+
- `click-outside-dropdown-menu(event)`
|
|
380
|
+
|
|
241
381
|
#### Checkbox
|
|
242
382
|
Customizable checkbox with multiple themes and validation support.
|
|
243
383
|
|
|
@@ -248,7 +388,8 @@ Customizable checkbox with multiple themes and validation support.
|
|
|
248
388
|
| v-model / modelValue | binding value - `true` when checked, `false` when unchecked | `boolean` | `false` |
|
|
249
389
|
| label | checkbox label | `string` | — |
|
|
250
390
|
| size | checkbox size | `'small' \| 'medium'` | `'medium'` |
|
|
251
|
-
| built_in_theme | color theme | `'green' \| 'primary-blue' \| 'secondary-blue'
|
|
391
|
+
| built_in_theme | built-in color theme | `'green' \| 'primary-blue' \| 'secondary-blue'` | `'green'` |
|
|
392
|
+
| customized_theme_color | custom theme color (overrides built_in_theme) | `string`(any valid CSS color value) | `''` |
|
|
252
393
|
| customized_class | custom CSS class | `string` | — |
|
|
253
394
|
| disabled | whether checkbox is disabled | `boolean` | `false` |
|
|
254
395
|
| autofocus | whether to auto focus | `boolean` | `false` |
|
|
@@ -266,15 +407,31 @@ Customizable checkbox with multiple themes and validation support.
|
|
|
266
407
|
|--------|-------------|------|
|
|
267
408
|
| focus | focus the checkbox | `() => void` |
|
|
268
409
|
| blur | blur the checkbox | `() => void` |
|
|
269
|
-
|
|
|
410
|
+
| error | show error message, will automatically use the error theme | `(message: string) => void` |
|
|
270
411
|
| removeAlertOrErrorEffect | clear error state | `() => void` |
|
|
271
412
|
|
|
272
413
|
```vue
|
|
273
414
|
<template>
|
|
415
|
+
<!-- the #FF0000 will override the built-in theme color -->
|
|
274
416
|
<Checkbox
|
|
275
417
|
v-model="agreed"
|
|
276
418
|
label="I agree to terms"
|
|
277
419
|
built_in_theme="primary-blue"
|
|
420
|
+
customized_theme_color="#FF0000"
|
|
421
|
+
@change="handleChange"
|
|
422
|
+
/>
|
|
423
|
+
<!-- if want to use global defined color as the theme color, can use var() -->
|
|
424
|
+
<Checkbox
|
|
425
|
+
v-model="agreed"
|
|
426
|
+
label="I agree to terms"
|
|
427
|
+
customized_theme_color="var(--Schemes-primary)"
|
|
428
|
+
@change="handleChange"
|
|
429
|
+
/>
|
|
430
|
+
<!-- Theme color can be any form of CSS color value. Since if the theme color isn't empty, it will override the built-in theme color. The background color of the checked and indeterminate state, and the mask color of the interaction effects of the checkbox will be the theme color, while the border color of unchecked checkbox will always be hsl(226, 8%, 10%). But when the checkbox is in error state, the theme will be error theme (the border color of unchecked checkbox, the background color of the checked and indeterminate state, and the mask color of the interaction effects of the checkbox will always be --semantic-error-color--) -->
|
|
431
|
+
<Checkbox
|
|
432
|
+
v-model="agreed"
|
|
433
|
+
label="I agree to terms"
|
|
434
|
+
customized_theme_color="hsl(3, 71%, 40%)"
|
|
278
435
|
@change="handleChange"
|
|
279
436
|
/>
|
|
280
437
|
</template>
|
|
@@ -306,8 +463,86 @@ Container for managing multiple related checkboxes as a group.
|
|
|
306
463
|
const selectedItems = ref(['option1'])
|
|
307
464
|
const options = ['option1', 'option2', 'option3']
|
|
308
465
|
</script>
|
|
466
|
+
|
|
467
|
+
#### DropdownMenu
|
|
468
|
+
Public dropdown menu primitive with nested dropdown support and responsive behavior (web vs. mobile). The companion `DropdownMenuItem` remains internal and is not exposed publically.
|
|
469
|
+
|
|
470
|
+
##### Attributes
|
|
471
|
+
|
|
472
|
+
| Attribute | Description | Type | Default |
|
|
473
|
+
|-----------|-------------|------|---------|
|
|
474
|
+
| options | menu options tree (see schema below) | `Array<Option>` | `[]` |
|
|
475
|
+
| size | menu item sizing | `'small' \| 'medium' \| 'large'` | `'small'` |
|
|
476
|
+
| width_type | width behavior | `'fill-whole' \| 'fit-content'` | `'fill-whole'` |
|
|
477
|
+
| with_box_shadow | whether the menu container has a shadow | `boolean` | `true` |
|
|
478
|
+
|
|
479
|
+
Option schema (recursive):
|
|
480
|
+
|
|
481
|
+
```
|
|
482
|
+
type Option = {
|
|
483
|
+
value: string,
|
|
484
|
+
label: string,
|
|
485
|
+
// Optional visuals (priority: slot > raw HTML > image URL handled inside items)
|
|
486
|
+
prefix?: string,
|
|
487
|
+
suffix?: string,
|
|
488
|
+
prefix_slot_raw_html_content?: string,
|
|
489
|
+
suffix_slot_raw_html_content?: string,
|
|
490
|
+
// Nesting
|
|
491
|
+
options?: Option[],
|
|
492
|
+
// Visual divider hint for the following item
|
|
493
|
+
has_divider?: boolean,
|
|
494
|
+
// Disabled state
|
|
495
|
+
disabled?: boolean,
|
|
496
|
+
}
|
|
309
497
|
```
|
|
310
498
|
|
|
499
|
+
Behavior notes:
|
|
500
|
+
|
|
501
|
+
- On wide screens (width > 1024), nested dropdowns open as side menus on hover/click with smart positioning.
|
|
502
|
+
- On small screens (≤ 1024), a single column flow is used; nested levels replace the list view using an internal history stack and a back affordance.
|
|
503
|
+
- The component emits a click-outside event when the user clicks outside of the root dropdown.
|
|
504
|
+
|
|
505
|
+
##### Events
|
|
506
|
+
|
|
507
|
+
| Event | Description | Parameters |
|
|
508
|
+
|-------|-------------|------------|
|
|
509
|
+
| select-dropdown-option | emitted when an option without further nesting is selected | `(value: string, in_dropdown_level: number)` |
|
|
510
|
+
| click-outside-dropdown-menu | emitted when clicking outside the root menu | `—` |
|
|
511
|
+
|
|
512
|
+
##### Usage
|
|
513
|
+
|
|
514
|
+
```vue
|
|
515
|
+
<template>
|
|
516
|
+
<DropdownMenu
|
|
517
|
+
:options="options"
|
|
518
|
+
:size="'small'"
|
|
519
|
+
:width_type="'fill-whole'"
|
|
520
|
+
@click-outside-dropdown-menu="onOutside"
|
|
521
|
+
@select-dropdown-option="onSelect"
|
|
522
|
+
/>
|
|
523
|
+
</template>
|
|
524
|
+
|
|
525
|
+
<script setup>
|
|
526
|
+
import { DropdownMenu } from 'pns-component-library'
|
|
527
|
+
|
|
528
|
+
const options = [
|
|
529
|
+
{
|
|
530
|
+
value: 'option1',
|
|
531
|
+
label: 'Option 1',
|
|
532
|
+
options: [
|
|
533
|
+
{ value: 'option1.1', label: 'Option 1.1' },
|
|
534
|
+
{ value: 'option1.2', label: 'Option 1.2', options: [
|
|
535
|
+
{ value: 'option1.2.1', label: 'Option 1.2.1' },
|
|
536
|
+
]},
|
|
537
|
+
],
|
|
538
|
+
},
|
|
539
|
+
{ value: 'option2', label: 'Option 2' },
|
|
540
|
+
]
|
|
541
|
+
|
|
542
|
+
function onOutside(){ /* handle outside click */ }
|
|
543
|
+
function onSelect(value, level){ /* handle select */ }
|
|
544
|
+
</script>
|
|
545
|
+
|
|
311
546
|
#### Radio
|
|
312
547
|
Radio button component for single selection from multiple options.
|
|
313
548
|
|
|
@@ -349,56 +584,265 @@ Radio button component for single selection from multiple options.
|
|
|
349
584
|
<Radio v-model="single" value="yes" label="Agree to terms" />
|
|
350
585
|
```
|
|
351
586
|
|
|
587
|
+
#### Badge
|
|
588
|
+
Notification badge component with customizable size, position, themes, and content display. Features automatic text color calculation for optimal contrast.
|
|
589
|
+
|
|
590
|
+
##### Attributes
|
|
591
|
+
|
|
592
|
+
| Attribute | Description | Type | Default |
|
|
593
|
+
|-----------|-------------|------|---------|
|
|
594
|
+
| content | badge content (number or text) | `string \| number` | `''` |
|
|
595
|
+
| size | badge size | `'small' \| 'medium' \| 'large'` | `'small'` |
|
|
596
|
+
| position_type | badge position relative to content | `'top-right' \| 'center-right'` | `'top-right'` |
|
|
597
|
+
| customized_top_distance | top offset for top-right position (CSS length) | `string` | `''` |
|
|
598
|
+
| customized_right_distance | right offset for top-right position (CSS length) | `string` | `''` |
|
|
599
|
+
| built_in_theme | color theme | `'primary' \| 'secondary'` | `'primary'` |
|
|
600
|
+
| customized_theme_color | custom background color (any valid CSS color) | `string` | `''` |
|
|
601
|
+
| customized_class | custom CSS class | `string` | `''` |
|
|
602
|
+
| disabled | hide the badge without affecting the wrapped content | `boolean` | `false` |
|
|
603
|
+
|
|
604
|
+
##### Size Specifications
|
|
605
|
+
|
|
606
|
+
| Size | Web (px) | Mobile ≤767px (px) | With Content |
|
|
607
|
+
|------|----------|-------------------|--------------|
|
|
608
|
+
| small | 6×6 | 6×6 | auto-sized |
|
|
609
|
+
| medium | 8×8 | 6×6 | auto-sized |
|
|
610
|
+
| large | 16×16 | 16×16 | auto-sized |
|
|
611
|
+
|
|
612
|
+
> **Note**: When badge has content, it automatically becomes large with padding (min-width: 20px, height: 20px).
|
|
613
|
+
|
|
614
|
+
##### Position Types
|
|
615
|
+
|
|
616
|
+
| Position | Behavior |
|
|
617
|
+
|----------|----------|
|
|
618
|
+
| top-right | Positioned absolutely at top-right corner (0, 0) |
|
|
619
|
+
| center-right | Inline-flex aligned at right side with 4px gap |
|
|
620
|
+
|
|
621
|
+
> **Note**: Use padding on the wrapper div to control badge position. The badge positions relative to the wrapper's content.
|
|
622
|
+
|
|
623
|
+
Position customization (top-right only):
|
|
624
|
+
|
|
625
|
+
- You can fine-tune the distance of a top-right badge using `customized_top_distance` and `customized_right_distance`. These map to internal CSS variables and only apply when `position_type="top-right"`.
|
|
626
|
+
|
|
627
|
+
```vue
|
|
628
|
+
<Badge position_type="top-right" customized_top_distance="8px" customized_right_distance="12px" content="3">
|
|
629
|
+
<div style="padding: 8px;">
|
|
630
|
+
<div>Icon</div>
|
|
631
|
+
</div>
|
|
632
|
+
|
|
633
|
+
</Badge>
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
##### Built-in Themes
|
|
637
|
+
|
|
638
|
+
| Theme | Background | Text Color |
|
|
639
|
+
|-------|-----------|------------|
|
|
640
|
+
| primary | #AF251D (red) | #FFFFFF (white) |
|
|
641
|
+
| secondary | #CFE0FC (light blue) | #083D91 (dark blue) |
|
|
642
|
+
|
|
643
|
+
##### Custom Theme Color
|
|
644
|
+
|
|
645
|
+
When using `customized_theme_color`, the text color is automatically calculated based on the background lightness:
|
|
646
|
+
- Light backgrounds (lightness > 50%) → dark text
|
|
647
|
+
- Dark backgrounds (lightness ≤ 50%) → light text
|
|
648
|
+
|
|
649
|
+
The algorithm adjusts lightness by ±60 while preserving hue and saturation for optimal contrast and color harmony.
|
|
650
|
+
|
|
651
|
+
```vue
|
|
652
|
+
<template>
|
|
653
|
+
<!-- Basic badge with sizes -->
|
|
654
|
+
<Badge size="small">
|
|
655
|
+
<div style="padding: 4px;">
|
|
656
|
+
<div>Icon</div>
|
|
657
|
+
</div>
|
|
658
|
+
</Badge>
|
|
659
|
+
|
|
660
|
+
<!-- Badge with content -->
|
|
661
|
+
<Badge content="5">
|
|
662
|
+
<div style="padding: 8px;">
|
|
663
|
+
<div>Icon</div>
|
|
664
|
+
</div>
|
|
665
|
+
</Badge>
|
|
666
|
+
|
|
667
|
+
<!-- Badge with position types -->
|
|
668
|
+
<Badge position_type="top-right" content="3">
|
|
669
|
+
<div style="padding: 8px;">
|
|
670
|
+
<div>Icon</div>
|
|
671
|
+
</div>
|
|
672
|
+
</Badge>
|
|
673
|
+
|
|
674
|
+
<!-- Badge with custom color -->
|
|
675
|
+
<Badge customized_theme_color="#10B981" content="New">
|
|
676
|
+
<div style="padding: 8px;">
|
|
677
|
+
<div>Icon</div>
|
|
678
|
+
</div>
|
|
679
|
+
</Badge>
|
|
680
|
+
|
|
681
|
+
<!-- Dark background example -->
|
|
682
|
+
<Badge customized_theme_color="hsl(220, 70%, 25%)" content="Dark">
|
|
683
|
+
<div style="padding: 8px;">
|
|
684
|
+
<div>Icon</div>
|
|
685
|
+
</div>
|
|
686
|
+
</Badge>
|
|
687
|
+
</template>
|
|
688
|
+
```
|
|
689
|
+
|
|
352
690
|
### Interactive Components
|
|
353
691
|
|
|
354
|
-
####
|
|
355
|
-
|
|
692
|
+
#### DynamicColorResponsiveButton
|
|
693
|
+
Modern button with dynamic color logic and responsive behavior.
|
|
694
|
+
|
|
695
|
+
##### Attributes
|
|
696
|
+
|
|
697
|
+
| Attribute | Description | Type | Default |
|
|
698
|
+
|-----------|-------------|------|---------|
|
|
699
|
+
| button_type | button style variant | `'filled' \| 'outlined' \| 'text' \| 'elevated'` | `'filled'` |
|
|
700
|
+
| button_border_radius | CSS border-radius value | `string` | `'100px'` |
|
|
701
|
+
| built_in_theme | built-in color theme | `'primary' \| 'secondary' \| 'tertiary' \| 'primary-container' \| 'secondary-container' \| 'tertiary-container'` | `'primary'` |
|
|
702
|
+
| customized_theme_color | overrides built_in_theme with any CSS color (e.g., `#3d5c8f`, `rgb(...)`, `hsl(...)`, `var(--Schemes-primary)`) | `string` | `''` |
|
|
703
|
+
| customized_class | custom CSS class for outer container | `string` | `''` |
|
|
704
|
+
| width_type | width mode | `'fill-whole' \| 'fit-content'` (auto-handles icon-only with `narrow`/`wide`) | `'fit-content'` |
|
|
705
|
+
| button_id | explicit id used in emitted event; falls back to `display_name` | `string` | `''` |
|
|
706
|
+
| display_name | button text (used when default slot empty) | `string` | `'button name'` |
|
|
707
|
+
| prefix | image url for prefix when not using slot | `string` | `''` |
|
|
708
|
+
| suffix | image url for suffix when not using slot | `string` | `''` |
|
|
709
|
+
| prefix_slot_raw_html_content | raw html for prefix (when not using named slot) | `string` | `''` |
|
|
710
|
+
| suffix_slot_raw_html_content | raw html for suffix (when not using named slot) | `string` | `''` |
|
|
711
|
+
| disabled | disable interaction | `boolean` | `false` |
|
|
712
|
+
| has_interaction_effect | enable hover/focus/active visual effects (mask and slight radius changes). Set to `false` to keep the button static (useful when embedding purely for coloring icons/slots) | `boolean` | `true` |
|
|
713
|
+
| size | size (Web/Tablet enum) | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'small'` |
|
|
714
|
+
|
|
715
|
+
##### Events
|
|
716
|
+
|
|
717
|
+
| Event | Description | Parameters |
|
|
718
|
+
|-------|-------------|------------|
|
|
719
|
+
| click-button | emitted on button click | `(button_id: string, display_name: string)` |
|
|
720
|
+
|
|
721
|
+
##### Exposes
|
|
722
|
+
|
|
723
|
+
| Method | Description | Type |
|
|
724
|
+
|--------|-------------|------|
|
|
725
|
+
| handleFocusButton | programmatically apply focus style | `() => void` |
|
|
726
|
+
| handleBlurButton | remove focus style | `() => void` |
|
|
727
|
+
|
|
728
|
+
##### Slots
|
|
729
|
+
|
|
730
|
+
| Name | Description |
|
|
731
|
+
|------|-------------|
|
|
732
|
+
| prefix | content before the label (e.g., icon) |
|
|
733
|
+
| default | label content; falls back to `display_name` |
|
|
734
|
+
| suffix | content after the label (e.g., icon) |
|
|
735
|
+
|
|
736
|
+
```vue
|
|
737
|
+
<template>
|
|
738
|
+
<DynamicColorResponsiveButton
|
|
739
|
+
button_id="save-changes"
|
|
740
|
+
display_name="Save Changes"
|
|
741
|
+
button_type="filled"
|
|
742
|
+
built_in_theme="primary"
|
|
743
|
+
:has_interaction_effect="true"
|
|
744
|
+
width_type="fit-content"
|
|
745
|
+
@click-button="onSave"
|
|
746
|
+
>
|
|
747
|
+
<template #prefix>
|
|
748
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M5 12h14v2H5z"/></svg>
|
|
749
|
+
</template>
|
|
750
|
+
</DynamicColorResponsiveButton>
|
|
751
|
+
</template>
|
|
752
|
+
|
|
753
|
+
<script setup>
|
|
754
|
+
function onSave(id, name){
|
|
755
|
+
console.log('clicked:', id, name)
|
|
756
|
+
}
|
|
757
|
+
</script>
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
#### FloatingActionButton
|
|
761
|
+
Mobile-only floating action button that anchors to the bottom-right of the viewport on small screens. It supports three sizes, built-in themes or a custom theme color, named slots for icons, and an optional one-level options menu. The button is automatically hidden on wide screens (when `window.width > 1024`).
|
|
356
762
|
|
|
357
763
|
##### Attributes
|
|
358
764
|
|
|
359
765
|
| Attribute | Description | Type | Default |
|
|
360
766
|
|-----------|-------------|------|---------|
|
|
361
767
|
| display_name | button text | `string` | `'button'` |
|
|
362
|
-
| size | button size | `'
|
|
363
|
-
|
|
|
364
|
-
|
|
|
365
|
-
| customized_class | custom CSS class | `string` |
|
|
366
|
-
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
768
|
+
| size | button size | `'small' \| 'medium' \| 'large'` | `'small'` |
|
|
769
|
+
| build_in_theme | built-in color theme | `'primary' \| 'secondary' \| 'tertiary' \| 'primary-container' \| 'secondary-container' \| 'tertiary-container'` | `'primary'` |
|
|
770
|
+
| customized_theme_color | custom theme color (overrides built_in_theme). Accepts any valid CSS color string (e.g., `#3d5c8f`, `rgb(61, 92, 143)`, `hsl(217, 40%, 40%)`, or `var(--Schemes-primary)`). | `string` | `''` |
|
|
771
|
+
| customized_class | custom CSS class for the outer container | `string` | `''` |
|
|
772
|
+
| options | optional menu options (one level). Each item is `{ value: any, label: string, prefix?: string, suffix?: string }`. `prefix`/`suffix` are icon URLs for the option. | `array` | `[]` |
|
|
773
|
+
|
|
774
|
+
> Notes
|
|
775
|
+
> - When `options` is non-empty, clicking the FAB toggles the options menu. A cross button is provided to close the menu.
|
|
776
|
+
> - The component is hidden on wide screens (`width > 1024`) by design to target mobile usage.
|
|
777
|
+
> - `customized_theme_color` overrides `build_in_theme`. If it is not provided (empty string), the `build_in_theme` color will be used instead.
|
|
778
|
+
> - `customized_theme_color` accepts any valid CSS color value: e.g. `hsl(217, 40%, 40%)`, `#ff0000`, `rgb(255, 0, 0)`, `rgba(255, 0, 0, 0.5)`, `hsla(217, 40%, 40%, 0.5)`, or `var(--Schemes-primary)`.
|
|
779
|
+
> - When using `customized_theme_color`:
|
|
780
|
+
> - The FAB background color uses the provided value directly.
|
|
781
|
+
> - Related colors (text color, inline SVG color via currentColor, menu cross button colors, menu child button colors, and interaction masks) are dynamically calculated based on the `customized_theme_color` to ensure contrast and visual harmony.
|
|
370
782
|
|
|
371
783
|
##### Events
|
|
372
784
|
|
|
373
785
|
| Event | Description | Parameters |
|
|
374
786
|
|-------|-------------|------------|
|
|
375
|
-
| click |
|
|
376
|
-
|
|
|
377
|
-
| add-nested-dropdown-history-stack | triggers when nested dropdown is navigated | `(option: object)` |
|
|
378
|
-
| remove-nested-dropdown-history-stack | triggers when navigating back in nested dropdown | — |
|
|
787
|
+
| floating-action-button-click | emitted when the floating action button is clicked. If `options` is provided, this also toggles the menu visibility. | — |
|
|
788
|
+
| option-click | emitted when a menu option is clicked | `(value: any)` |
|
|
379
789
|
|
|
380
790
|
##### Slots
|
|
381
791
|
|
|
382
792
|
| Name | Description |
|
|
383
793
|
|------|-------------|
|
|
384
|
-
| prefix | content before button text |
|
|
385
|
-
|
|
|
794
|
+
| prefix | content rendered before the button text. Commonly used for an icon. |
|
|
795
|
+
| default | default slot for button label content. Falls back to `display_name` when empty. |
|
|
796
|
+
| suffix | content rendered after the button text. Commonly used for an icon. |
|
|
386
797
|
|
|
387
798
|
```vue
|
|
388
799
|
<script setup>
|
|
389
|
-
|
|
800
|
+
const quickCreateOptions = [
|
|
801
|
+
{ value: 'photo', label: 'New Photo' },
|
|
802
|
+
{ value: 'video', label: 'New Video' },
|
|
803
|
+
{ value: 'doc', label: 'New Doc' },
|
|
804
|
+
]
|
|
805
|
+
|
|
806
|
+
const handleFabClick = () => {
|
|
807
|
+
console.log('FAB clicked')
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
const handleOptionClick = (val) => {
|
|
811
|
+
console.log('Option clicked:', val)
|
|
812
|
+
}
|
|
390
813
|
</script>
|
|
814
|
+
|
|
391
815
|
<template>
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
816
|
+
<!-- Will only be visible on small screens (hidden on width > 1024) -->
|
|
817
|
+
<FloatingActionButton
|
|
818
|
+
display_name="New"
|
|
819
|
+
size="small"
|
|
820
|
+
build_in_theme="tertiary"
|
|
821
|
+
:options="quickCreateOptions"
|
|
822
|
+
@floating-action-button-click="handleFabClick"
|
|
823
|
+
@option-click="handleOptionClick"
|
|
397
824
|
>
|
|
398
825
|
<template #prefix>
|
|
399
|
-
|
|
826
|
+
<!-- Example icon (uses currentColor to adapt) -->
|
|
827
|
+
<!-- Pros of using inline SVG:
|
|
828
|
+
- Inherits currentColor automatically, so it adapts to theme/text color without extra CSS
|
|
829
|
+
- Reacts naturally to hover/active color-mix effects used by the component
|
|
830
|
+
- No additional network request for external assets
|
|
831
|
+
Using a regular <img src="..."> is also fine if you prefer image assets;
|
|
832
|
+
just note that <img> won't inherit currentColor by default (you'd need pre-colored assets or additional CSS). -->
|
|
833
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
|
834
|
+
<path d="M6 16L10 12.95L14 16L12.5 11.05L16.5 8.2H11.6L10 3L8.4 8.2H3.5L7.5 11.05L6 16ZM10 20C8.61667 20 7.31667 19.7375 6.1 19.2125C4.88333 18.6875 3.825 17.975 2.925 17.075C2.025 16.175 1.3125 15.1167 0.7875 13.9C0.2625 12.6833 0 11.3833 0 10C0 8.61667 0.2625 7.31667 0.7875 6.1C1.3125 4.88333 2.025 3.825 2.925 2.925C3.825 2.025 4.88333 1.3125 6.1 0.7875C7.31667 0.2625 8.61667 0 10 0C11.3833 0 12.6833 0.2625 13.9 0.7875C15.1167 1.3125 16.175 2.025 17.075 2.925C17.975 3.825 18.6875 4.88333 19.2125 6.1C19.7375 7.31667 20 8.61667 20 10C20 11.3833 19.7375 12.6833 19.2125 13.9C18.6875 15.1167 17.975 16.175 17.075 17.075C16.175 17.975 15.1167 18.6875 13.9 19.2125C12.6833 19.7375 11.3833 20 10 20Z" fill="currentColor"/>
|
|
835
|
+
</svg>
|
|
400
836
|
</template>
|
|
401
|
-
</
|
|
837
|
+
</FloatingActionButton>
|
|
838
|
+
|
|
839
|
+
<!-- Custom theme color overrides built-in theme -->
|
|
840
|
+
<FloatingActionButton
|
|
841
|
+
display_name="Custom"
|
|
842
|
+
size="medium"
|
|
843
|
+
customized_theme_color="hsl(217, 40%, 40%)"
|
|
844
|
+
@floating-action-button-click="handleFabClick"
|
|
845
|
+
/>
|
|
402
846
|
</template>
|
|
403
847
|
```
|
|
404
848
|
|
|
@@ -599,7 +1043,7 @@ The library provides multiple built-in themes:
|
|
|
599
1043
|
|
|
600
1044
|
```vue
|
|
601
1045
|
<Checkbox built_in_theme="primary-blue" />
|
|
602
|
-
<
|
|
1046
|
+
<DynamicColorResponsiveButton button_type="filled" built_in_theme="primary" />
|
|
603
1047
|
```
|
|
604
1048
|
|
|
605
1049
|
## 📱 Responsive Design
|