diff --git a/docs/vue.md b/docs/vue.md index 60c0e21..d65ea3c 100644 --- a/docs/vue.md +++ b/docs/vue.md @@ -105,4 +105,44 @@ See [Vue Component Map](vue/component-map.md). +## Layout And Data Patterns + +Second-wave adapter components cover common application surfaces: + +```vue + + + + + + + + + + + + + + Vue + Stable + + +``` + For AI agents and project-specific rules, see [Vue AI Usage Guide](vue/ai-usage-guide.md). diff --git a/docs/vue/ai-usage-guide.md b/docs/vue/ai-usage-guide.md index 9ac4c71..51e8cfb 100644 --- a/docs/vue/ai-usage-guide.md +++ b/docs/vue/ai-usage-guide.md @@ -51,6 +51,18 @@ - global notification: `GnToastProvider` + `useToast` - confirmation: `GnConfirmDialog` - structured rows: `GnTable` +- toolbar/action row: `GnToolbar` +- input addon layout: `GnInputGroup` +- search input: `GnSearchField` +- pagination: `GnPagination` +- empty result panel: `GnEmptyState` +- loading placeholders: `GnSkeleton` +- key-value metadata: `GnDescriptionList` +- determinate progress: `GnProgress` +- wizard/staged process: `GnSteps` +- chips/filters: `GnChip`, `GnChipGroup` +- user or entity identity: `GnAvatar`, `GnIdentity`, `GnAvatarStack` +- activity timeline/log: `GnTimeline`, `GnActivityLog` ## Do Not diff --git a/docs/vue/component-map.md b/docs/vue/component-map.md index 4ac73f6..cd66b03 100644 --- a/docs/vue/component-map.md +++ b/docs/vue/component-map.md @@ -22,6 +22,22 @@ | `GnToastProvider` | `.toast` | Provides `useToast()`. | | `GnConfirmDialog` | `GnModal` + actions | Emits `confirm` / `cancel`. | | `GnTable` | `.table`, `.table-wrapper` | Columns/rows with scoped cell slots. | +| `GnToolbar` | `.toolbar` | Title/meta with action slot. | +| `GnInputGroup` | `.input-group` | Addon/default/action slots. | +| `GnSearchField` | `.input-group.search-field` | Search input with clear action. | +| `GnPagination` | `.pagination` | `v-model:page` style via `update:page`. | +| `GnEmptyState` | `.empty-state` | Icon, title, text, actions. | +| `GnSkeleton` | `.skeleton`, `.skeleton-stack` | Loading placeholders. | +| `GnDescriptionList` | `.description-list` | Key-value metadata. | +| `GnProgress` | `.progress` | Determinate progress. | +| `GnSteps` | `.steps` | Wizard/staged process. | +| `GnChip` | `.chip` | Static/selectable/removable chip. | +| `GnChipGroup` | `.chip-group` | Chip layout wrapper. | +| `GnAvatar` | `.avatar` | Initials/icon/image/status. | +| `GnIdentity` | `.identity` | Avatar + title/meta row. | +| `GnAvatarStack` | `.avatar-stack` | Compact avatar group. | +| `GnTimeline` | `.timeline` | Activity/event timeline. | +| `GnActivityLog` | `.activity-log` | Compact log rows. | ## Variant Names diff --git a/examples/vue/src/main.js b/examples/vue/src/main.js index 3f37cc2..4791aef 100644 --- a/examples/vue/src/main.js +++ b/examples/vue/src/main.js @@ -3,10 +3,20 @@ import "gnexus-ui-kit/dist/assets/fonts/phosphor-icons/src/css/icons.css"; import { GnButton, + GnActivityLog, + GnAvatar, + GnBadge, + GnChip, + GnChipGroup, + GnDescriptionList, + GnEmptyState, GnInput, GnModal, GnPageHeader, + GnProgress, + GnSearchField, GnTabs, + GnToolbar, GnToastProvider, useToast } from "gnexus-ui-kit/vue"; @@ -14,21 +24,40 @@ const DemoScreen = { components: { GnButton, + GnActivityLog, + GnAvatar, + GnBadge, + GnChip, + GnChipGroup, + GnDescriptionList, + GnEmptyState, GnInput, GnModal, GnPageHeader, + GnProgress, + GnSearchField, GnTabs }, setup() { const activeTab = ref("overview"); const modalOpen = ref(false); const name = ref("Launch Plan"); + const query = ref(""); const toast = useToast(); const tabs = [ { id: "overview", label: "Overview", icon: "ph-chart-bar" }, { id: "activity", label: "Activity", icon: "ph-clock" } ]; + const details = [ + { key: "owner", term: "Owner", value: "Ops Console" }, + { key: "status", term: "Status", value: "Active" }, + { key: "region", term: "Region", value: "eu-central" } + ]; + const activity = [ + { key: "created", time: "10:12", title: "Workspace created" }, + { key: "synced", time: "10:18", title: "Tokens synced" } + ]; const save = () => { toast.success({ title: "Saved", text: `${name.value} updated` }); @@ -37,8 +66,11 @@ return { activeTab, + activity, + details, modalOpen, name, + query, tabs, save }; @@ -59,16 +91,47 @@ + + + + + + + + - Use adapter components instead of local one-off markup. + + + + + + + Vue + Stable + Contract + + - Interactive state belongs to Vue; visual classes stay in the kit. + + + log + + + + + + + Ready + + + + diff --git a/src/vue/components/GnActivityLog.js b/src/vue/components/GnActivityLog.js new file mode 100644 index 0000000..51acfb4 --- /dev/null +++ b/src/vue/components/GnActivityLog.js @@ -0,0 +1,18 @@ +import { defineComponent, h } from "vue"; +import { cx } from "../utils.js"; + +export default defineComponent({ + name: "GnActivityLog", + props: { + items: { type: Array, default: () => [] } + }, + setup(props, { attrs, slots }) { + return () => h("div", { ...attrs, class: cx("activity-log", attrs.class) }, props.items.map(item => h("div", { + class: "activity-log-row" + }, [ + h("time", { class: "activity-log-time" }, item.time), + h("span", { class: "activity-log-title" }, slots[item.key]?.({ item }) || item.title), + slots.actions && h("span", {}, slots.actions({ item })) + ]))); + } +}); diff --git a/src/vue/components/GnAvatar.js b/src/vue/components/GnAvatar.js new file mode 100644 index 0000000..785a30e --- /dev/null +++ b/src/vue/components/GnAvatar.js @@ -0,0 +1,36 @@ +import { defineComponent, h } from "vue"; +import { cx, iconNode, normalizeVariant } from "../utils.js"; + +export default defineComponent({ + name: "GnAvatar", + props: { + src: { type: String, default: "" }, + alt: { type: String, default: "" }, + initials: { type: String, default: "" }, + icon: { type: String, default: "" }, + size: { type: String, default: "md" }, + variant: { type: String, default: "primary" }, + outline: { type: Boolean, default: false }, + status: { type: String, default: "" } + }, + setup(props, { attrs }) { + return () => { + const variant = normalizeVariant(props.variant); + + return h("span", { + ...attrs, + class: cx("avatar", `avatar-${variant}`, { + "avatar-sm": props.size === "sm", + "avatar-lg": props.size === "lg", + "avatar-outline": props.outline, + "is-online": props.status === "online", + "is-busy": props.status === "busy", + "is-offline": props.status === "offline" + }, attrs.class) + }, [ + props.src ? h("img", { src: props.src, alt: props.alt }) : iconNode(props.icon) || props.initials, + props.status && h("span", { class: "avatar-status", "aria-hidden": "true" }) + ]); + }; + } +}); diff --git a/src/vue/components/GnAvatarStack.js b/src/vue/components/GnAvatarStack.js new file mode 100644 index 0000000..0b7ee39 --- /dev/null +++ b/src/vue/components/GnAvatarStack.js @@ -0,0 +1,17 @@ +import { defineComponent, h } from "vue"; +import GnAvatar from "./GnAvatar.js"; + +export default defineComponent({ + name: "GnAvatarStack", + props: { + items: { type: Array, default: () => [] }, + count: { type: [Number, String], default: "" } + }, + setup(props, { slots }) { + return () => h("span", { class: "avatar-stack" }, [ + props.items.map(item => h(GnAvatar, { ...item, size: item.size || "sm" })), + slots.default?.(), + props.count && h("span", { class: "avatar-stack-count" }, `+${props.count}`) + ]); + } +}); diff --git a/src/vue/components/GnChip.js b/src/vue/components/GnChip.js new file mode 100644 index 0000000..99a613c --- /dev/null +++ b/src/vue/components/GnChip.js @@ -0,0 +1,43 @@ +import { defineComponent, h } from "vue"; +import { cx, iconNode, normalizeVariant } from "../utils.js"; + +export default defineComponent({ + name: "GnChip", + props: { + variant: { type: String, default: "" }, + icon: { type: String, default: "" }, + selected: { type: Boolean, default: false }, + disabled: { type: Boolean, default: false }, + removable: { type: Boolean, default: false } + }, + emits: ["remove"], + setup(props, { attrs, emit, slots }) { + return () => { + const tag = attrs.onClick ? "button" : "span"; + const variant = props.variant ? normalizeVariant(props.variant) : ""; + + return h(tag, { + ...attrs, + type: tag === "button" ? "button" : undefined, + disabled: tag === "button" ? props.disabled : undefined, + "aria-pressed": tag === "button" ? String(props.selected) : undefined, + class: cx("chip", variant && `chip-${variant}`, { + "chip-selected": props.selected, + "chip-disabled": props.disabled + }, attrs.class) + }, [ + iconNode(props.icon), + slots.default?.(), + props.removable && h("button", { + class: "chip-remove", + type: "button", + "aria-label": "Remove", + onClick: event => { + event.stopPropagation(); + emit("remove"); + } + }, [iconNode("ph-x")]) + ]); + }; + } +}); diff --git a/src/vue/components/GnChipGroup.js b/src/vue/components/GnChipGroup.js new file mode 100644 index 0000000..b9d8542 --- /dev/null +++ b/src/vue/components/GnChipGroup.js @@ -0,0 +1,9 @@ +import { defineComponent, h } from "vue"; +import { cx } from "../utils.js"; + +export default defineComponent({ + name: "GnChipGroup", + setup(_, { attrs, slots }) { + return () => h("div", { ...attrs, class: cx("chip-group", attrs.class) }, slots.default?.()); + } +}); diff --git a/src/vue/components/GnDescriptionList.js b/src/vue/components/GnDescriptionList.js new file mode 100644 index 0000000..8d467ec --- /dev/null +++ b/src/vue/components/GnDescriptionList.js @@ -0,0 +1,21 @@ +import { defineComponent, h } from "vue"; +import { cx } from "../utils.js"; + +export default defineComponent({ + name: "GnDescriptionList", + props: { + items: { type: Array, default: () => [] }, + compact: { type: Boolean, default: false } + }, + setup(props, { attrs, slots }) { + return () => h("dl", { + ...attrs, + class: cx("description-list", { "description-list-compact": props.compact }, attrs.class) + }, props.items.map(item => h("div", { class: "description-list-row" }, [ + h("dt", { class: "description-list-term" }, item.term || item.label), + h("dd", { class: cx("description-list-value", item.muted && "description-list-value-muted") }, + slots[item.key]?.({ item }) || item.value + ) + ]))); + } +}); diff --git a/src/vue/components/GnEmptyState.js b/src/vue/components/GnEmptyState.js new file mode 100644 index 0000000..7b6ec65 --- /dev/null +++ b/src/vue/components/GnEmptyState.js @@ -0,0 +1,23 @@ +import { defineComponent, h } from "vue"; +import { cx, iconNode } from "../utils.js"; + +export default defineComponent({ + name: "GnEmptyState", + props: { + title: { type: String, required: true }, + text: { type: String, default: "" }, + icon: { type: String, default: "ph-package" }, + variant: { type: String, default: "" } + }, + setup(props, { attrs, slots }) { + return () => h("div", { + ...attrs, + class: cx("empty-state", props.variant && `empty-state-${props.variant}`, attrs.class) + }, [ + h("div", { class: "empty-state-icon" }, [iconNode(props.icon)]), + h("h3", { class: "empty-state-title" }, slots.title?.() || props.title), + (props.text || slots.default) && h("p", { class: "empty-state-text" }, slots.default?.() || props.text), + slots.actions && h("div", { class: "empty-state-actions" }, slots.actions()) + ]); + } +}); diff --git a/src/vue/components/GnIdentity.js b/src/vue/components/GnIdentity.js new file mode 100644 index 0000000..20aee2f --- /dev/null +++ b/src/vue/components/GnIdentity.js @@ -0,0 +1,20 @@ +import { defineComponent, h } from "vue"; +import GnAvatar from "./GnAvatar.js"; + +export default defineComponent({ + name: "GnIdentity", + props: { + title: { type: String, required: true }, + meta: { type: String, default: "" }, + avatar: { type: Object, default: () => ({}) } + }, + setup(props, { slots }) { + return () => h("span", { class: "identity" }, [ + slots.avatar?.() || h(GnAvatar, props.avatar), + h("span", { class: "identity-content" }, [ + h("span", { class: "identity-title" }, slots.title?.() || props.title), + (props.meta || slots.meta) && h("span", { class: "identity-meta" }, slots.meta?.() || props.meta) + ]) + ]); + } +}); diff --git a/src/vue/components/GnInputGroup.js b/src/vue/components/GnInputGroup.js new file mode 100644 index 0000000..99e8a73 --- /dev/null +++ b/src/vue/components/GnInputGroup.js @@ -0,0 +1,21 @@ +import { defineComponent, h } from "vue"; +import { cx, iconNode } from "../utils.js"; + +export default defineComponent({ + name: "GnInputGroup", + props: { + compact: { type: Boolean, default: false }, + addon: { type: String, default: "" }, + icon: { type: String, default: "" } + }, + setup(props, { attrs, slots }) { + return () => h("div", { + ...attrs, + class: cx("input-group", { "input-group-compact": props.compact }, attrs.class) + }, [ + (props.addon || props.icon || slots.addon) && h("span", { class: "input-group-addon" }, slots.addon?.() || iconNode(props.icon) || props.addon), + slots.default?.(), + slots.action && h("span", { class: "input-group-action" }, slots.action()) + ]); + } +}); diff --git a/src/vue/components/GnPagination.js b/src/vue/components/GnPagination.js new file mode 100644 index 0000000..454b568 --- /dev/null +++ b/src/vue/components/GnPagination.js @@ -0,0 +1,44 @@ +import { defineComponent, h } from "vue"; +import { cx, iconNode } from "../utils.js"; + +export default defineComponent({ + name: "GnPagination", + props: { + page: { type: Number, required: true }, + totalPages: { type: Number, required: true }, + ariaLabel: { type: String, default: "Pagination" } + }, + emits: ["update:page"], + setup(props, { emit }) { + const setPage = page => { + if(page >= 1 && page <= props.totalPages && page !== props.page) { + emit("update:page", page); + } + }; + + return () => { + const pages = Array.from({ length: props.totalPages }, (_, index) => index + 1); + + return h("nav", { class: "pagination", "aria-label": props.ariaLabel }, [ + h("button", { + class: "pagination-item", + type: "button", + disabled: props.page <= 1, + onClick: () => setPage(props.page - 1) + }, [iconNode("ph-arrow-left")]), + pages.map(page => h("button", { + class: cx("pagination-item", { "pagination-item-active": page === props.page }), + type: "button", + "aria-current": page === props.page ? "page" : undefined, + onClick: () => setPage(page) + }, page)), + h("button", { + class: "pagination-item", + type: "button", + disabled: props.page >= props.totalPages, + onClick: () => setPage(props.page + 1) + }, [iconNode("ph-arrow-right")]) + ]); + }; + } +}); diff --git a/src/vue/components/GnProgress.js b/src/vue/components/GnProgress.js new file mode 100644 index 0000000..6c7350a --- /dev/null +++ b/src/vue/components/GnProgress.js @@ -0,0 +1,43 @@ +import { defineComponent, h } from "vue"; +import { cx, normalizeVariant } from "../utils.js"; + +export default defineComponent({ + name: "GnProgress", + props: { + value: { type: Number, required: true }, + max: { type: Number, default: 100 }, + label: { type: String, default: "" }, + variant: { type: String, default: "secondary" }, + striped: { type: Boolean, default: false }, + animated: { type: Boolean, default: false } + }, + setup(props, { attrs, slots }) { + return () => { + const percent = Math.max(0, Math.min(100, Math.round((props.value / props.max) * 100))); + const variant = normalizeVariant(props.variant, "secondary"); + + return h("div", { + ...attrs, + class: cx("progress", `progress-${variant}`, { + "progress-striped": props.striped, + "progress-animated": props.animated + }, attrs.class), + style: { "--progress-value": `${percent}%` } + }, [ + (props.label || slots.label) && h("div", { class: "progress-header" }, [ + h("span", {}, slots.label?.() || props.label), + h("span", { class: "progress-value" }, `${percent}%`) + ]), + h("div", { class: "progress-track" }, [ + h("span", { + class: "progress-bar", + role: "progressbar", + "aria-valuenow": props.value, + "aria-valuemin": 0, + "aria-valuemax": props.max + }) + ]) + ]); + }; + } +}); diff --git a/src/vue/components/GnSearchField.js b/src/vue/components/GnSearchField.js new file mode 100644 index 0000000..42de021 --- /dev/null +++ b/src/vue/components/GnSearchField.js @@ -0,0 +1,40 @@ +import { defineComponent, h } from "vue"; +import { cx, eventValue, iconNode } from "../utils.js"; + +export default defineComponent({ + name: "GnSearchField", + inheritAttrs: false, + props: { + modelValue: { type: String, default: "" }, + placeholder: { type: String, default: "Search" }, + compact: { type: Boolean, default: true }, + clearable: { type: Boolean, default: true } + }, + emits: ["update:modelValue", "clear"], + setup(props, { attrs, emit }) { + const clear = () => { + emit("update:modelValue", ""); + emit("clear"); + }; + + return () => h("div", { + class: cx("input-group search-field", { "input-group-compact": props.compact }) + }, [ + h("span", { class: "input-group-addon" }, [iconNode("ph-magnifying-glass")]), + h("input", { + ...attrs, + type: "search", + value: props.modelValue, + placeholder: props.placeholder, + class: cx("input-group-input", attrs.class), + onInput: event => emit("update:modelValue", eventValue(event)) + }), + props.clearable && h("button", { + class: "input-group-action", + type: "button", + "aria-label": "Clear search", + onClick: clear + }, [iconNode("ph-x")]) + ]); + } +}); diff --git a/src/vue/components/GnSkeleton.js b/src/vue/components/GnSkeleton.js new file mode 100644 index 0000000..db9e3e5 --- /dev/null +++ b/src/vue/components/GnSkeleton.js @@ -0,0 +1,22 @@ +import { defineComponent, h } from "vue"; +import { cx } from "../utils.js"; + +export default defineComponent({ + name: "GnSkeleton", + props: { + type: { type: String, default: "line" }, + stack: { type: Boolean, default: false }, + count: { type: Number, default: 1 } + }, + setup(props, { attrs }) { + const skeleton = key => h("span", { + key, + ...attrs, + class: cx("skeleton", `skeleton-${props.type}`, attrs.class) + }); + + return () => props.stack + ? h("div", { class: "skeleton-stack" }, Array.from({ length: props.count }, (_, index) => skeleton(index))) + : skeleton(0); + } +}); diff --git a/src/vue/components/GnSteps.js b/src/vue/components/GnSteps.js new file mode 100644 index 0000000..1e18393 --- /dev/null +++ b/src/vue/components/GnSteps.js @@ -0,0 +1,26 @@ +import { defineComponent, h } from "vue"; +import { cx } from "../utils.js"; + +export default defineComponent({ + name: "GnSteps", + props: { + items: { type: Array, required: true }, + vertical: { type: Boolean, default: false } + }, + setup(props, { attrs }) { + return () => h("ol", { + ...attrs, + class: cx("steps", { "steps-vertical": props.vertical }, attrs.class) + }, props.items.map((item, index) => h("li", { + class: cx("step", { + "step-complete": item.status === "complete", + "step-current": item.status === "current", + "step-disabled": item.disabled || item.status === "disabled" + }) + }, [ + h("span", { class: "step-marker" }, item.marker || String(index + 1)), + h("h3", { class: "step-title" }, item.title), + item.text && h("p", { class: "step-text" }, item.text) + ]))); + } +}); diff --git a/src/vue/components/GnTimeline.js b/src/vue/components/GnTimeline.js new file mode 100644 index 0000000..bbad18d --- /dev/null +++ b/src/vue/components/GnTimeline.js @@ -0,0 +1,28 @@ +import { defineComponent, h } from "vue"; +import { cx, iconNode, normalizeVariant } from "../utils.js"; + +export default defineComponent({ + name: "GnTimeline", + props: { + items: { type: Array, default: () => [] } + }, + setup(props, { attrs, slots }) { + return () => h("ol", { ...attrs, class: cx("timeline", attrs.class) }, props.items.map(item => { + const variant = item.variant ? normalizeVariant(item.variant) : ""; + + return h("li", { class: cx("timeline-item", variant && `timeline-item-${variant}`) }, [ + h("span", { class: "timeline-marker" }, [iconNode(item.icon || "ph-circle")]), + h("div", { class: "timeline-content" }, [ + h("article", { class: "timeline-card" }, [ + h("header", { class: "timeline-header" }, [ + h("h3", { class: "timeline-title" }, item.title), + item.time && h("time", { class: "timeline-time" }, item.time) + ]), + h("p", { class: "timeline-text" }, slots[item.key]?.({ item }) || item.text), + (item.meta || slots.meta) && h("div", { class: "timeline-meta" }, slots.meta?.({ item }) || item.meta) + ]) + ]) + ]); + })); + } +}); diff --git a/src/vue/components/GnToolbar.js b/src/vue/components/GnToolbar.js new file mode 100644 index 0000000..12cc1dd --- /dev/null +++ b/src/vue/components/GnToolbar.js @@ -0,0 +1,21 @@ +import { defineComponent, h } from "vue"; +import { cx } from "../utils.js"; + +export default defineComponent({ + name: "GnToolbar", + props: { + title: { type: String, default: "" }, + meta: { type: String, default: "" } + }, + setup(props, { attrs, slots }) { + return () => h("div", { ...attrs, class: cx("toolbar", attrs.class) }, [ + h("div", { class: "toolbar-group" }, [ + h("div", {}, [ + (props.title || slots.title) && h("h3", { class: "toolbar-title" }, slots.title?.() || props.title), + (props.meta || slots.meta) && h("span", { class: "toolbar-meta" }, slots.meta?.() || props.meta) + ]) + ]), + (slots.default || slots.actions) && h("div", { class: "toolbar-group" }, slots.actions?.() || slots.default?.()) + ]); + } +}); diff --git a/src/vue/index.js b/src/vue/index.js index f9e3987..28f0164 100644 --- a/src/vue/index.js +++ b/src/vue/index.js @@ -1,20 +1,36 @@ export { default as GnAccordion } from "./components/GnAccordion.js"; +export { default as GnActivityLog } from "./components/GnActivityLog.js"; export { default as GnAlert } from "./components/GnAlert.js"; +export { default as GnAvatar } from "./components/GnAvatar.js"; +export { default as GnAvatarStack } from "./components/GnAvatarStack.js"; export { default as GnBadge } from "./components/GnBadge.js"; export { default as GnButton } from "./components/GnButton.js"; export { default as GnCard } from "./components/GnCard.js"; export { default as GnCheckbox } from "./components/GnCheckbox.js"; +export { default as GnChip } from "./components/GnChip.js"; +export { default as GnChipGroup } from "./components/GnChipGroup.js"; export { default as GnConfirmDialog } from "./components/GnConfirmDialog.js"; +export { default as GnDescriptionList } from "./components/GnDescriptionList.js"; export { default as GnDrawer } from "./components/GnDrawer.js"; +export { default as GnEmptyState } from "./components/GnEmptyState.js"; export { default as GnIconButton } from "./components/GnIconButton.js"; +export { default as GnIdentity } from "./components/GnIdentity.js"; export { default as GnInput } from "./components/GnInput.js"; +export { default as GnInputGroup } from "./components/GnInputGroup.js"; export { default as GnModal } from "./components/GnModal.js"; export { default as GnPageHeader } from "./components/GnPageHeader.js"; +export { default as GnPagination } from "./components/GnPagination.js"; +export { default as GnProgress } from "./components/GnProgress.js"; +export { default as GnSearchField } from "./components/GnSearchField.js"; export { default as GnSelect } from "./components/GnSelect.js"; +export { default as GnSkeleton } from "./components/GnSkeleton.js"; +export { default as GnSteps } from "./components/GnSteps.js"; export { default as GnSwitch } from "./components/GnSwitch.js"; export { default as GnTable } from "./components/GnTable.js"; export { default as GnTabs } from "./components/GnTabs.js"; export { default as GnTextarea } from "./components/GnTextarea.js"; +export { default as GnTimeline } from "./components/GnTimeline.js"; +export { default as GnToolbar } from "./components/GnToolbar.js"; export { default as GnToastProvider } from "./components/GnToastProvider.js"; export { useToast } from "./composables/useToast.js"; export { components, default as GnexusUiVue } from "./plugin.js"; diff --git a/src/vue/plugin.js b/src/vue/plugin.js index e63f661..87e5286 100644 --- a/src/vue/plugin.js +++ b/src/vue/plugin.js @@ -1,40 +1,72 @@ import GnAccordion from "./components/GnAccordion.js"; +import GnActivityLog from "./components/GnActivityLog.js"; import GnAlert from "./components/GnAlert.js"; +import GnAvatar from "./components/GnAvatar.js"; +import GnAvatarStack from "./components/GnAvatarStack.js"; import GnBadge from "./components/GnBadge.js"; import GnButton from "./components/GnButton.js"; import GnCard from "./components/GnCard.js"; import GnCheckbox from "./components/GnCheckbox.js"; +import GnChip from "./components/GnChip.js"; +import GnChipGroup from "./components/GnChipGroup.js"; import GnConfirmDialog from "./components/GnConfirmDialog.js"; +import GnDescriptionList from "./components/GnDescriptionList.js"; import GnDrawer from "./components/GnDrawer.js"; +import GnEmptyState from "./components/GnEmptyState.js"; import GnIconButton from "./components/GnIconButton.js"; +import GnIdentity from "./components/GnIdentity.js"; import GnInput from "./components/GnInput.js"; +import GnInputGroup from "./components/GnInputGroup.js"; import GnModal from "./components/GnModal.js"; import GnPageHeader from "./components/GnPageHeader.js"; +import GnPagination from "./components/GnPagination.js"; +import GnProgress from "./components/GnProgress.js"; +import GnSearchField from "./components/GnSearchField.js"; import GnSelect from "./components/GnSelect.js"; +import GnSkeleton from "./components/GnSkeleton.js"; +import GnSteps from "./components/GnSteps.js"; import GnSwitch from "./components/GnSwitch.js"; import GnTable from "./components/GnTable.js"; import GnTabs from "./components/GnTabs.js"; import GnTextarea from "./components/GnTextarea.js"; +import GnTimeline from "./components/GnTimeline.js"; +import GnToolbar from "./components/GnToolbar.js"; import GnToastProvider from "./components/GnToastProvider.js"; export const components = { GnAccordion, + GnActivityLog, GnAlert, + GnAvatar, + GnAvatarStack, GnBadge, GnButton, GnCard, GnCheckbox, + GnChip, + GnChipGroup, GnConfirmDialog, + GnDescriptionList, GnDrawer, + GnEmptyState, GnIconButton, + GnIdentity, GnInput, + GnInputGroup, GnModal, GnPageHeader, + GnPagination, + GnProgress, + GnSearchField, GnSelect, + GnSkeleton, + GnSteps, GnSwitch, GnTable, GnTabs, GnTextarea, + GnTimeline, + GnToolbar, GnToastProvider };
Use adapter components instead of local one-off markup.
Interactive state belongs to Vue; visual classes stay in the kit.