@vuecs/button 1.0.3 → 1.1.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.
- package/README.md +33 -4
- package/dist/component.d.ts +58 -9
- package/dist/component.d.ts.map +1 -1
- package/dist/index.mjs +29 -4
- package/dist/index.mjs.map +1 -1
- package/dist/vue.d.ts +1 -1
- package/dist/vue.d.ts.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,16 +1,45 @@
|
|
|
1
1
|
# @vuecs/button
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@vuecs/button)
|
|
4
|
+
[](https://github.com/Tada5hi/vuecs/actions/workflows/main.yml)
|
|
5
|
+
[](./LICENSE)
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
**`<VCButton>` — the general-purpose button of [vuecs](https://github.com/tada5hi/vuecs).** A full `variant` × `color` × `size` matrix, loading state, and icon slots — visually defined entirely by whichever theme you install (Tailwind, Bootstrap, Bulma, or your own).
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
## ✨ What's inside
|
|
10
|
+
|
|
11
|
+
- 🎛️ **Variant matrix** — `variant` (`solid` / `outline` / `soft` / `ghost` / `link`) × `color` (`primary` / `neutral` / `success` / `warning` / `error` / `info`) × `size` (`sm` / `md` / `lg`), resolved through the vuecs variant system.
|
|
12
|
+
- ⏳ **Loading state** — `:loading` disables interaction and exposes `{ loading, disabled }` to the default slot for spinners or custom feedback.
|
|
13
|
+
- 🖼️ **Icon support** — `icon-left` / `icon-right` Iconify-name props (rendered via `<VCIcon>`) plus leading/trailing slots for full control.
|
|
14
|
+
- 🔗 **Polymorphic** — render as `button`, `a`, any tag, or a component (`RouterLink` / `NuxtLink`) via `:as`; native `type` forwarding for forms, `aria-disabled` for non-button targets.
|
|
15
|
+
- 📝 **`useSubmitButton()` companion** — `@vuecs/forms` ships an experimental composable that drives create/update submit buttons (text, icon, color) from global behavioral defaults.
|
|
16
|
+
|
|
17
|
+
## 📦 Installation
|
|
9
18
|
|
|
10
19
|
```bash
|
|
11
20
|
npm install @vuecs/button
|
|
12
21
|
```
|
|
13
22
|
|
|
23
|
+
## ⚡ Usage
|
|
24
|
+
|
|
25
|
+
```vue
|
|
26
|
+
<VCButton color="primary" icon-left="lucide:plus" :loading="busy" @click="create">
|
|
27
|
+
Create
|
|
28
|
+
</VCButton>
|
|
29
|
+
|
|
30
|
+
<VCButton variant="outline" color="error" size="sm">Delete</VCButton>
|
|
31
|
+
<VCButton variant="ghost" as="a" href="/docs">Docs</VCButton>
|
|
32
|
+
|
|
33
|
+
<!-- button-styled router link -->
|
|
34
|
+
<VCButton :as="RouterLink" :to="`/clients/${id}`" color="primary" variant="outline" size="sm">
|
|
35
|
+
Edit
|
|
36
|
+
</VCButton>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 📚 Documentation
|
|
40
|
+
|
|
41
|
+
Full reference, live demos, and per-theme variant tables: **[vuecs.dev/components/button](https://vuecs.dev/components/button)**
|
|
42
|
+
|
|
14
43
|
## License
|
|
15
44
|
|
|
16
45
|
Made with 💚
|
package/dist/component.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentThemeDefinition, ThemeClassesOverride, ThemeElementDefinition, VariantValues } from '@vuecs/core';
|
|
2
|
-
import type { ExtractPublicPropTypes, PropType, SlotsType } from 'vue';
|
|
2
|
+
import type { Component, ExtractPublicPropTypes, PropType, SlotsType } from 'vue';
|
|
3
3
|
import type { ButtonColor, ButtonSize, ButtonSlotProps, ButtonThemeClasses, ButtonVariant } from './type';
|
|
4
4
|
declare module '@vuecs/core' {
|
|
5
5
|
interface ThemeElements {
|
|
@@ -24,10 +24,26 @@ declare const buttonProps: {
|
|
|
24
24
|
type: StringConstructor;
|
|
25
25
|
default: string;
|
|
26
26
|
};
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Element or component to render as. Pass a string tag (`'a'`, `'div'`)
|
|
29
|
+
* or a component (`RouterLink` / `NuxtLink`) to render a button-styled
|
|
30
|
+
* link / arbitrary element. Native `type` / `disabled` semantics apply
|
|
31
|
+
* only when this resolves to `'button'`; every other target receives
|
|
32
|
+
* `aria-disabled` instead. Extra attrs (`to`, `href`, `target`, …)
|
|
33
|
+
* forward to the rendered element.
|
|
34
|
+
*/
|
|
35
|
+
as: {
|
|
36
|
+
type: PropType<string | Component>;
|
|
29
37
|
default: string;
|
|
30
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* @deprecated Use `as` instead. Retained as a non-breaking alias — when
|
|
41
|
+
* set it takes precedence over `as`.
|
|
42
|
+
*/
|
|
43
|
+
tag: {
|
|
44
|
+
type: PropType<string | Component>;
|
|
45
|
+
default: any;
|
|
46
|
+
};
|
|
31
47
|
label: {
|
|
32
48
|
type: StringConstructor;
|
|
33
49
|
default: any;
|
|
@@ -75,10 +91,26 @@ export declare const VCButton: import("vue").DefineComponent<import("vue").Extra
|
|
|
75
91
|
type: StringConstructor;
|
|
76
92
|
default: string;
|
|
77
93
|
};
|
|
78
|
-
|
|
79
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Element or component to render as. Pass a string tag (`'a'`, `'div'`)
|
|
96
|
+
* or a component (`RouterLink` / `NuxtLink`) to render a button-styled
|
|
97
|
+
* link / arbitrary element. Native `type` / `disabled` semantics apply
|
|
98
|
+
* only when this resolves to `'button'`; every other target receives
|
|
99
|
+
* `aria-disabled` instead. Extra attrs (`to`, `href`, `target`, …)
|
|
100
|
+
* forward to the rendered element.
|
|
101
|
+
*/
|
|
102
|
+
as: {
|
|
103
|
+
type: PropType<string | Component>;
|
|
80
104
|
default: string;
|
|
81
105
|
};
|
|
106
|
+
/**
|
|
107
|
+
* @deprecated Use `as` instead. Retained as a non-breaking alias — when
|
|
108
|
+
* set it takes precedence over `as`.
|
|
109
|
+
*/
|
|
110
|
+
tag: {
|
|
111
|
+
type: PropType<string | Component>;
|
|
112
|
+
default: any;
|
|
113
|
+
};
|
|
82
114
|
label: {
|
|
83
115
|
type: StringConstructor;
|
|
84
116
|
default: any;
|
|
@@ -126,10 +158,26 @@ export declare const VCButton: import("vue").DefineComponent<import("vue").Extra
|
|
|
126
158
|
type: StringConstructor;
|
|
127
159
|
default: string;
|
|
128
160
|
};
|
|
129
|
-
|
|
130
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Element or component to render as. Pass a string tag (`'a'`, `'div'`)
|
|
163
|
+
* or a component (`RouterLink` / `NuxtLink`) to render a button-styled
|
|
164
|
+
* link / arbitrary element. Native `type` / `disabled` semantics apply
|
|
165
|
+
* only when this resolves to `'button'`; every other target receives
|
|
166
|
+
* `aria-disabled` instead. Extra attrs (`to`, `href`, `target`, …)
|
|
167
|
+
* forward to the rendered element.
|
|
168
|
+
*/
|
|
169
|
+
as: {
|
|
170
|
+
type: PropType<string | Component>;
|
|
131
171
|
default: string;
|
|
132
172
|
};
|
|
173
|
+
/**
|
|
174
|
+
* @deprecated Use `as` instead. Retained as a non-breaking alias — when
|
|
175
|
+
* set it takes precedence over `as`.
|
|
176
|
+
*/
|
|
177
|
+
tag: {
|
|
178
|
+
type: PropType<string | Component>;
|
|
179
|
+
default: any;
|
|
180
|
+
};
|
|
133
181
|
label: {
|
|
134
182
|
type: StringConstructor;
|
|
135
183
|
default: any;
|
|
@@ -160,11 +208,12 @@ export declare const VCButton: import("vue").DefineComponent<import("vue").Extra
|
|
|
160
208
|
};
|
|
161
209
|
}>> & Readonly<{}>, {
|
|
162
210
|
label: string;
|
|
211
|
+
size: ButtonSize;
|
|
163
212
|
color: ButtonColor;
|
|
164
213
|
variant: ButtonVariant;
|
|
165
|
-
size: ButtonSize;
|
|
166
214
|
type: string;
|
|
167
|
-
|
|
215
|
+
as: string;
|
|
216
|
+
tag: any;
|
|
168
217
|
iconLeft: string;
|
|
169
218
|
iconRight: string;
|
|
170
219
|
loading: boolean;
|
package/dist/component.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACR,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,EAEtB,aAAa,EAChB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EACR,sBAAsB,EACtB,QAAQ,EACR,SAAS,EAEZ,MAAM,KAAK,CAAC;AAMb,OAAO,KAAK,EACR,WAAW,EACX,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,aAAa,EAChB,MAAM,QAAQ,CAAC;AAEhB,OAAO,QAAQ,aAAa,CAAC;IACzB,UAAU,aAAa;QACnB,MAAM,CAAC,EAAE,sBAAsB,CAAC,kBAAkB,CAAC,CAAC;KACvD;CACJ;AAED,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,CAAC,kBAAkB,CAO5E,CAAC;AAEF,QAAA,MAAM,WAAW;;cACY,QAAQ,CAAC,WAAW,CAAC;;;;cACnB,QAAQ,CAAC,aAAa,CAAC;;;;cAC1B,QAAQ,CAAC,UAAU,CAAC
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACR,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,EAEtB,aAAa,EAChB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EACR,SAAS,EACT,sBAAsB,EACtB,QAAQ,EACR,SAAS,EAEZ,MAAM,KAAK,CAAC;AAMb,OAAO,KAAK,EACR,WAAW,EACX,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,aAAa,EAChB,MAAM,QAAQ,CAAC;AAEhB,OAAO,QAAQ,aAAa,CAAC;IACzB,UAAU,aAAa;QACnB,MAAM,CAAC,EAAE,sBAAsB,CAAC,kBAAkB,CAAC,CAAC;KACvD;CACJ;AAED,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,CAAC,kBAAkB,CAO5E,CAAC;AAEF,QAAA,MAAM,WAAW;;cACY,QAAQ,CAAC,WAAW,CAAC;;;;cACnB,QAAQ,CAAC,aAAa,CAAC;;;;cAC1B,QAAQ,CAAC,UAAU,CAAC;;;;;;;IAE5C;;;;;;;OAOG;;cACuC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC;;;IACtE;;;OAGG;;cACwC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;cAMzC,QAAQ,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,CAAC;;;;cAChD,QAAQ,CAAC,aAAa,CAAC;;;CAC1D,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,sBAAsB,CAAC,OAAO,WAAW,CAAC,CAAC;AAErE,eAAO,MAAM,QAAQ;;cA7BQ,QAAQ,CAAC,WAAW,CAAC;;;;cACnB,QAAQ,CAAC,aAAa,CAAC;;;;cAC1B,QAAQ,CAAC,UAAU,CAAC;;;;;;;IAE5C;;;;;;;OAOG;;cACuC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC;;;IACtE;;;OAGG;;cACwC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;cAMzC,QAAQ,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,CAAC;;;;cAChD,QAAQ,CAAC,aAAa,CAAC;;;;;;;cAxB9B,QAAQ,CAAC,WAAW,CAAC;;;;cACnB,QAAQ,CAAC,aAAa,CAAC;;;;cAC1B,QAAQ,CAAC,UAAU,CAAC;;;;;;;IAE5C;;;;;;;OAOG;;cACuC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC;;;IACtE;;;OAGG;;cACwC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;cAMzC,QAAQ,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,CAAC;;;;cAChD,QAAQ,CAAC,aAAa,CAAC;;;;;;;;;;;;;;;;;;aAS1C,eAAe;aACf,eAAe;cACd,eAAe;yEA2H/B,CAAC"}
|
package/dist/index.mjs
CHANGED
|
@@ -27,10 +27,34 @@ const VCButton = defineComponent({
|
|
|
27
27
|
type: String,
|
|
28
28
|
default: "button"
|
|
29
29
|
},
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Element or component to render as. Pass a string tag (`'a'`, `'div'`)
|
|
32
|
+
* or a component (`RouterLink` / `NuxtLink`) to render a button-styled
|
|
33
|
+
* link / arbitrary element. Native `type` / `disabled` semantics apply
|
|
34
|
+
* only when this resolves to `'button'`; every other target receives
|
|
35
|
+
* `aria-disabled` instead. Extra attrs (`to`, `href`, `target`, …)
|
|
36
|
+
* forward to the rendered element.
|
|
37
|
+
*/
|
|
38
|
+
as: {
|
|
39
|
+
type: [
|
|
40
|
+
String,
|
|
41
|
+
Object,
|
|
42
|
+
Function
|
|
43
|
+
],
|
|
32
44
|
default: "button"
|
|
33
45
|
},
|
|
46
|
+
/**
|
|
47
|
+
* @deprecated Use `as` instead. Retained as a non-breaking alias — when
|
|
48
|
+
* set it takes precedence over `as`.
|
|
49
|
+
*/
|
|
50
|
+
tag: {
|
|
51
|
+
type: [
|
|
52
|
+
String,
|
|
53
|
+
Object,
|
|
54
|
+
Function
|
|
55
|
+
],
|
|
56
|
+
default: void 0
|
|
57
|
+
},
|
|
34
58
|
label: {
|
|
35
59
|
type: String,
|
|
36
60
|
default: void 0
|
|
@@ -100,8 +124,9 @@ const VCButton = defineComponent({
|
|
|
100
124
|
const trailingOut = slots.trailing(slotProps);
|
|
101
125
|
if (isNonEmptySlot(trailingOut)) children.push(h("span", { class: resolved.trailing || void 0 }, trailingOut));
|
|
102
126
|
} else if (props.iconRight) children.push(h("span", { class: resolved.trailing || void 0 }, [h(VCIcon, { name: props.iconRight })]));
|
|
103
|
-
const
|
|
104
|
-
|
|
127
|
+
const renderAs = props.tag ?? props.as;
|
|
128
|
+
const isNativeButton = renderAs === "button";
|
|
129
|
+
return h(renderAs, mergeProps({
|
|
105
130
|
class: [resolved.root || void 0, props.loading ? "vc-button--busy" : void 0],
|
|
106
131
|
...isNativeButton ? { type: props.type } : {},
|
|
107
132
|
...isNativeButton ? { disabled: isDisabled || void 0 } : {},
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/component.ts","../src/index.ts"],"sourcesContent":["import { useComponentTheme } from '@vuecs/core';\nimport type {\n ComponentThemeDefinition,\n ThemeClassesOverride,\n ThemeElementDefinition,\n UseComponentThemeProps,\n VariantValues,\n} from '@vuecs/core';\nimport { VCIcon } from '@vuecs/icon';\nimport type {\n ExtractPublicPropTypes,\n PropType,\n SlotsType,\n VNodeArrayChildren,\n} from 'vue';\nimport {\n defineComponent,\n h,\n mergeProps,\n} from 'vue';\nimport type {\n ButtonColor,\n ButtonSize,\n ButtonSlotProps,\n ButtonThemeClasses,\n ButtonVariant,\n} from './type';\n\ndeclare module '@vuecs/core' {\n interface ThemeElements {\n button?: ThemeElementDefinition<ButtonThemeClasses>;\n }\n}\n\nexport const buttonThemeDefaults: ComponentThemeDefinition<ButtonThemeClasses> = {\n classes: {\n root: 'vc-button',\n leading: 'vc-button-leading',\n trailing: 'vc-button-trailing',\n label: 'vc-button-label',\n },\n};\n\nconst buttonProps = {\n color: { type: String as PropType<ButtonColor>, default: undefined },\n variant: { type: String as PropType<ButtonVariant>, default: undefined },\n size: { type: String as PropType<ButtonSize>, default: undefined },\n type: { type: String, default: 'button' },\n tag: { type: String, default: 'button' },\n label: { type: String, default: undefined },\n iconLeft: { type: String, default: undefined },\n iconRight: { type: String, default: undefined },\n loading: { type: Boolean, default: false },\n disabled: { type: Boolean, default: false },\n themeClass: { type: Object as PropType<ThemeClassesOverride<ButtonThemeClasses>>, default: undefined },\n themeVariant: { type: Object as PropType<VariantValues>, default: undefined },\n};\n\nexport type ButtonProps = ExtractPublicPropTypes<typeof buttonProps>;\n\nexport const VCButton = defineComponent({\n name: 'VCButton',\n props: buttonProps,\n slots: Object as SlotsType<{\n default: ButtonSlotProps;\n leading: ButtonSlotProps;\n trailing: ButtonSlotProps;\n }>,\n setup(props, { attrs, slots }) {\n // The convenience props (color/variant/size/loading) are merged into\n // themeVariant before resolution so themes can drive slot classes off\n // them via the standard variant system. Getter properties keep this\n // reactive — Vue's computed() inside useComponentTheme tracks the\n // underlying prop reads.\n const themeProps: UseComponentThemeProps<ButtonThemeClasses> = {\n get themeClass() {\n return props.themeClass;\n },\n get themeVariant() {\n return {\n ...(props.themeVariant ?? {}),\n ...(props.color !== undefined ? { color: props.color } : {}),\n ...(props.variant !== undefined ? { variant: props.variant } : {}),\n ...(props.size !== undefined ? { size: props.size } : {}),\n ...(props.loading ? { loading: true } : {}),\n };\n },\n };\n\n const theme = useComponentTheme('button', themeProps, buttonThemeDefaults);\n\n return () => {\n const resolved = theme.value;\n const isDisabled = props.disabled || props.loading;\n const slotProps: ButtonSlotProps = {\n loading: props.loading,\n disabled: isDisabled,\n };\n\n const children: VNodeArrayChildren = [];\n\n // Empty slot results (`<template #leading />` returning []) used to\n // emit a wrapper <span> with no children — functionally inert but\n // dead markup. Coerce slot output to non-empty before pushing.\n const isNonEmptySlot = (out: unknown): out is VNodeArrayChildren => Array.isArray(out) && out.length > 0;\n\n // When loading, the leading slot becomes a spinner — universally\n // legible loading affordance, replaces any consumer-provided icon\n // for the duration of the in-flight work. Without this the only\n // signal was a faint opacity/cursor change that read identical to\n // the disabled state on most themes.\n //\n // Accessibility: the spinner glyph is `aria-hidden` (it's\n // decorative — the visual is the spinning ring) but we wrap it\n // alongside a visually-hidden \"Loading\" label so screen readers\n // announce the busy state. Combined with `aria-busy=\"true\"` on\n // the root (set below), AT users get a clear \"in progress\"\n // signal instead of the indistinct \"disabled\" the native\n // `disabled` attribute would otherwise convey on its own.\n if (props.loading) {\n children.push(h('span', { class: resolved.leading || undefined }, [\n h('span', { class: 'vc-button-spinner', 'aria-hidden': 'true' }),\n h('span', { class: 'vc-sr-only' }, 'Loading'),\n ]));\n } else if (slots.leading) {\n const leadingOut = slots.leading(slotProps);\n if (isNonEmptySlot(leadingOut)) {\n children.push(h('span', { class: resolved.leading || undefined }, leadingOut));\n }\n } else if (props.iconLeft) {\n // The string is treated as an Iconify name (e.g. 'lucide:plus')\n // and resolved through <VCIcon>. Consumers wanting raw class\n // strings should slot their own element via #leading instead.\n children.push(h('span', { class: resolved.leading || undefined }, [\n h(VCIcon, { name: props.iconLeft }),\n ]));\n }\n\n const slotLabel = slots.default ? slots.default(slotProps) : undefined;\n if (isNonEmptySlot(slotLabel)) {\n children.push(h('span', { class: resolved.label || undefined }, slotLabel));\n } else if (typeof props.label === 'string' && props.label !== '') {\n children.push(h('span', { class: resolved.label || undefined }, props.label));\n }\n\n if (slots.trailing) {\n const trailingOut = slots.trailing(slotProps);\n if (isNonEmptySlot(trailingOut)) {\n children.push(h('span', { class: resolved.trailing || undefined }, trailingOut));\n }\n } else if (props.iconRight) {\n children.push(h('span', { class: resolved.trailing || undefined }, [\n h(VCIcon, { name: props.iconRight }),\n ]));\n }\n\n const isNativeButton = props.tag === 'button';\n\n return h(\n props.tag,\n mergeProps({\n class: [\n resolved.root || undefined,\n // Structural busy class — themes layer their own look,\n // but every theme gets a consistent loading affordance\n // (wait cursor + opacity pulse) without redeclaring it.\n // `disabled` blocks pointer events on native buttons,\n // which would defeat `cursor: wait`; the CSS handles\n // that by scoping `cursor: wait` only when the busy\n // class is set and avoids `pointer-events: none`.\n props.loading ? 'vc-button--busy' : undefined,\n ],\n ...(isNativeButton ? { type: props.type } : {}),\n ...(isNativeButton ? { disabled: isDisabled || undefined } : {}),\n ...(!isNativeButton && isDisabled ? { 'aria-disabled': 'true' } : {}),\n // Distinguish loading from plain `disabled` for AT —\n // both still set `disabled` (loading must block clicks\n // to prevent double-submit), but `aria-busy` lets screen\n // readers announce \"busy\" rather than just \"disabled\".\n ...(props.loading ? { 'aria-busy': 'true' } : {}),\n }, attrs),\n children,\n );\n };\n },\n});\n","import { installDefaultsManager, installThemeManager } from '@vuecs/core';\nimport type { App, Plugin } from 'vue';\n\nimport '../assets/index.css';\nimport './vue';\n\nimport { VCButton } from './component';\nimport type { Options } from './type';\n\nexport * from './component';\nexport * from './type';\n\nexport function install(instance: App, options: Options = {}): void {\n installThemeManager(instance, options);\n installDefaultsManager(instance, options);\n instance.component('VCButton', VCButton);\n}\n\nexport default { install } satisfies Plugin<[Options?]>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/component.ts","../src/index.ts"],"sourcesContent":["import { useComponentTheme } from '@vuecs/core';\nimport type {\n ComponentThemeDefinition,\n ThemeClassesOverride,\n ThemeElementDefinition,\n UseComponentThemeProps,\n VariantValues,\n} from '@vuecs/core';\nimport { VCIcon } from '@vuecs/icon';\nimport type {\n Component,\n ExtractPublicPropTypes,\n PropType,\n SlotsType,\n VNodeArrayChildren,\n} from 'vue';\nimport {\n defineComponent,\n h,\n mergeProps,\n} from 'vue';\nimport type {\n ButtonColor,\n ButtonSize,\n ButtonSlotProps,\n ButtonThemeClasses,\n ButtonVariant,\n} from './type';\n\ndeclare module '@vuecs/core' {\n interface ThemeElements {\n button?: ThemeElementDefinition<ButtonThemeClasses>;\n }\n}\n\nexport const buttonThemeDefaults: ComponentThemeDefinition<ButtonThemeClasses> = {\n classes: {\n root: 'vc-button',\n leading: 'vc-button-leading',\n trailing: 'vc-button-trailing',\n label: 'vc-button-label',\n },\n};\n\nconst buttonProps = {\n color: { type: String as PropType<ButtonColor>, default: undefined },\n variant: { type: String as PropType<ButtonVariant>, default: undefined },\n size: { type: String as PropType<ButtonSize>, default: undefined },\n type: { type: String, default: 'button' },\n /**\n * Element or component to render as. Pass a string tag (`'a'`, `'div'`)\n * or a component (`RouterLink` / `NuxtLink`) to render a button-styled\n * link / arbitrary element. Native `type` / `disabled` semantics apply\n * only when this resolves to `'button'`; every other target receives\n * `aria-disabled` instead. Extra attrs (`to`, `href`, `target`, …)\n * forward to the rendered element.\n */\n as: { type: [String, Object, Function] as PropType<string | Component>, default: 'button' },\n /**\n * @deprecated Use `as` instead. Retained as a non-breaking alias — when\n * set it takes precedence over `as`.\n */\n tag: { type: [String, Object, Function] as PropType<string | Component>, default: undefined },\n label: { type: String, default: undefined },\n iconLeft: { type: String, default: undefined },\n iconRight: { type: String, default: undefined },\n loading: { type: Boolean, default: false },\n disabled: { type: Boolean, default: false },\n themeClass: { type: Object as PropType<ThemeClassesOverride<ButtonThemeClasses>>, default: undefined },\n themeVariant: { type: Object as PropType<VariantValues>, default: undefined },\n};\n\nexport type ButtonProps = ExtractPublicPropTypes<typeof buttonProps>;\n\nexport const VCButton = defineComponent({\n name: 'VCButton',\n props: buttonProps,\n slots: Object as SlotsType<{\n default: ButtonSlotProps;\n leading: ButtonSlotProps;\n trailing: ButtonSlotProps;\n }>,\n setup(props, { attrs, slots }) {\n // The convenience props (color/variant/size/loading) are merged into\n // themeVariant before resolution so themes can drive slot classes off\n // them via the standard variant system. Getter properties keep this\n // reactive — Vue's computed() inside useComponentTheme tracks the\n // underlying prop reads.\n const themeProps: UseComponentThemeProps<ButtonThemeClasses> = {\n get themeClass() {\n return props.themeClass;\n },\n get themeVariant() {\n return {\n ...(props.themeVariant ?? {}),\n ...(props.color !== undefined ? { color: props.color } : {}),\n ...(props.variant !== undefined ? { variant: props.variant } : {}),\n ...(props.size !== undefined ? { size: props.size } : {}),\n ...(props.loading ? { loading: true } : {}),\n };\n },\n };\n\n const theme = useComponentTheme('button', themeProps, buttonThemeDefaults);\n\n return () => {\n const resolved = theme.value;\n const isDisabled = props.disabled || props.loading;\n const slotProps: ButtonSlotProps = {\n loading: props.loading,\n disabled: isDisabled,\n };\n\n const children: VNodeArrayChildren = [];\n\n // Empty slot results (`<template #leading />` returning []) used to\n // emit a wrapper <span> with no children — functionally inert but\n // dead markup. Coerce slot output to non-empty before pushing.\n const isNonEmptySlot = (out: unknown): out is VNodeArrayChildren => Array.isArray(out) && out.length > 0;\n\n // When loading, the leading slot becomes a spinner — universally\n // legible loading affordance, replaces any consumer-provided icon\n // for the duration of the in-flight work. Without this the only\n // signal was a faint opacity/cursor change that read identical to\n // the disabled state on most themes.\n //\n // Accessibility: the spinner glyph is `aria-hidden` (it's\n // decorative — the visual is the spinning ring) but we wrap it\n // alongside a visually-hidden \"Loading\" label so screen readers\n // announce the busy state. Combined with `aria-busy=\"true\"` on\n // the root (set below), AT users get a clear \"in progress\"\n // signal instead of the indistinct \"disabled\" the native\n // `disabled` attribute would otherwise convey on its own.\n if (props.loading) {\n children.push(h('span', { class: resolved.leading || undefined }, [\n h('span', { class: 'vc-button-spinner', 'aria-hidden': 'true' }),\n h('span', { class: 'vc-sr-only' }, 'Loading'),\n ]));\n } else if (slots.leading) {\n const leadingOut = slots.leading(slotProps);\n if (isNonEmptySlot(leadingOut)) {\n children.push(h('span', { class: resolved.leading || undefined }, leadingOut));\n }\n } else if (props.iconLeft) {\n // The string is treated as an Iconify name (e.g. 'lucide:plus')\n // and resolved through <VCIcon>. Consumers wanting raw class\n // strings should slot their own element via #leading instead.\n children.push(h('span', { class: resolved.leading || undefined }, [\n h(VCIcon, { name: props.iconLeft }),\n ]));\n }\n\n const slotLabel = slots.default ? slots.default(slotProps) : undefined;\n if (isNonEmptySlot(slotLabel)) {\n children.push(h('span', { class: resolved.label || undefined }, slotLabel));\n } else if (typeof props.label === 'string' && props.label !== '') {\n children.push(h('span', { class: resolved.label || undefined }, props.label));\n }\n\n if (slots.trailing) {\n const trailingOut = slots.trailing(slotProps);\n if (isNonEmptySlot(trailingOut)) {\n children.push(h('span', { class: resolved.trailing || undefined }, trailingOut));\n }\n } else if (props.iconRight) {\n children.push(h('span', { class: resolved.trailing || undefined }, [\n h(VCIcon, { name: props.iconRight }),\n ]));\n }\n\n // `tag` is the deprecated alias — when explicitly set it wins\n // over `as`; otherwise `as` (default `'button'`) drives the\n // render target. A string tag or a component both resolve here.\n const renderAs = props.tag ?? props.as;\n const isNativeButton = renderAs === 'button';\n\n return h(\n renderAs,\n mergeProps({\n class: [\n resolved.root || undefined,\n // Structural busy class — themes layer their own look,\n // but every theme gets a consistent loading affordance\n // (wait cursor + opacity pulse) without redeclaring it.\n // `disabled` blocks pointer events on native buttons,\n // which would defeat `cursor: wait`; the CSS handles\n // that by scoping `cursor: wait` only when the busy\n // class is set and avoids `pointer-events: none`.\n props.loading ? 'vc-button--busy' : undefined,\n ],\n ...(isNativeButton ? { type: props.type } : {}),\n ...(isNativeButton ? { disabled: isDisabled || undefined } : {}),\n ...(!isNativeButton && isDisabled ? { 'aria-disabled': 'true' } : {}),\n // Distinguish loading from plain `disabled` for AT —\n // both still set `disabled` (loading must block clicks\n // to prevent double-submit), but `aria-busy` lets screen\n // readers announce \"busy\" rather than just \"disabled\".\n ...(props.loading ? { 'aria-busy': 'true' } : {}),\n }, attrs),\n children,\n );\n };\n },\n});\n","import { installDefaultsManager, installThemeManager } from '@vuecs/core';\nimport type { App, Plugin } from 'vue';\n\nimport '../assets/index.css';\nimport './vue';\n\nimport { VCButton } from './component';\nimport type { Options } from './type';\n\nexport * from './component';\nexport * from './type';\n\nexport function install(instance: App, options: Options = {}): void {\n installThemeManager(instance, options);\n installDefaultsManager(instance, options);\n instance.component('VCButton', VCButton);\n}\n\nexport default { install } satisfies Plugin<[Options?]>;\n"],"mappings":"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"}
|
package/dist/vue.d.ts
CHANGED
package/dist/vue.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.d.ts","sourceRoot":"","sources":["../src/vue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,QAAQ,
|
|
1
|
+
{"version":3,"file":"vue.d.ts","sourceRoot":"","sources":["../src/vue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,QAAQ,KAAK,CAAC;IACjB,UAAiB,gBAAgB;QAC7B,QAAQ,EAAE,OAAO,QAAQ,CAAC;KAC7B;CACJ"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vuecs/button",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A general-purpose button component for Vue 3.",
|
|
6
6
|
"exports": {
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
"build": "rimraf dist && npm run build:js && npm run build:types"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@vuecs/core": "^3.1
|
|
44
|
-
"@vuecs/icon": "^1.0.
|
|
45
|
-
"vue": "^3.5.
|
|
43
|
+
"@vuecs/core": "^3.2.1",
|
|
44
|
+
"@vuecs/icon": "^1.0.2",
|
|
45
|
+
"vue": "^3.5.38"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@vuecs/core": "^3.1
|
|
49
|
-
"@vuecs/icon": "^1.0.
|
|
48
|
+
"@vuecs/core": "^3.2.1",
|
|
49
|
+
"@vuecs/icon": "^1.0.2",
|
|
50
50
|
"vue": "^3.x"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|