@ac-design/design-system 2.10.3 → 2.11.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # AppsCode Design System
2
2
 
3
- The design system for every AppsCode console (cluster-ui, kubedb-ui, platform-ui, billing, marketplace…): a **Tailwind CSS v4 theme** and **Vue 3 components**, with a documentation site that shows every component live.
3
+ The design system for every AppsCode console (cloud-ui, cluster-ui, kubedb-ui, platform-ui, billing-ui, selfhost-ui…): a **Tailwind CSS v4 theme** and **Vue 3 components**, with a documentation site that shows every component live.
4
4
 
5
5
  It replaces the old Bulma/SCSS library (`@appscode/design-system` 2.x). Every component the apps import from the old library has a replacement here, and the docs' Migration page maps the old props to the new ones.
6
6
 
@@ -14,9 +14,10 @@ npm run dev # docs site at http://localhost:5173
14
14
  | | |
15
15
  | --- | --- |
16
16
  | **Theme** | `src/lib/theme.css`: colour scales, semantic tokens (text, borders, surfaces), type scale, radii, shadows and a full dark theme, as a Tailwind `@theme`. |
17
- | **Components** | 85 Vue components plus 2 editors and 4 charts (counting sub-parts) in eight groups, listed below. |
18
- | **Editors** | `AcCodeEditor` and `AcFileEditor` on CodeMirror 6, in a separate entry so other apps don't bundle CodeMirror. |
19
- | **Composables** | `useColorMode()` (light/dark/system), `useToast()` (notifications), `useBrandColor()` (re-hue the brand at runtime). |
17
+ | **Components** | 101 Vue components (counting sub-parts such as `AcDropdownItem`) in eight groups, listed below. |
18
+ | **Editors** | `AcCodeEditor` and `AcFileEditor` on CodeMirror 6, in the `/editor` entry so other apps don't bundle CodeMirror. |
19
+ | **Charts** | Seven charts on Apache ECharts 6 (cloud-ui's engine), in the `/charts` entry. `echarts` is an optional peer dependency. |
20
+ | **Composables** | `useColorMode()` (light/dark/system), `useFontScale()` (text size), `useToast()` (notifications), `useBrandColor()` (re-hue the brand at runtime), `useTablePagination()` and `useApiPagination()`, `useBulkSelection()`, `useFormValidation()`, and `copyText()`. |
20
21
  | **Logo** | `AcLogo`: the AppsCode logo and mark as inline SVG that follows dark mode. |
21
22
  | **Docs site** | Getting started, foundations, a page per component (live playground, examples, guidelines, accessibility, API tables) and full example pages. |
22
23
 
@@ -24,15 +25,15 @@ npm run dev # docs site at http://localhost:5173
24
25
 
25
26
  | Group | Components |
26
27
  | --- | --- |
27
- | Element | Button, Button Group, Badge, Tag, Avatar, Alert, Banner, Card, Divider, Kbd, Copy Button |
28
- | Feedback | Spinner, Preloader, Skeleton, Progress, Circular Progress, Toast, Empty State |
29
- | Form | Input, Number Input, Tag Input, Key-Value Input, Textarea, Code Editor, File Editor, Select, CheckBox, CheckRadio, Switch, Switch Card, Search Bar, File Upload, Slider, Date Picker, Time Picker, Color Input, Form, Form Array, Theme Mode |
30
- | Overlay | Modal, Confirm Modal, Delete Modal, Side Panel, Dropdown, Tooltip |
31
- | Layout | Sidebar, Navbar, Header, Content Table, Section, Accordion, Status Bar, Footer |
32
- | Navigation | Breadcrumb, Tabs, Steps, Pagination, Side Tabs, App Switcher, Cluster Switcher, Notifications, Locale Switcher |
33
- | Data | Table, Bulk Action Bar, Info Table, Cell Value |
34
- | Cards | Stat Card, Feature Card, Resource Card, Usage Card |
35
- | Charts (`/charts`) | Line Chart, Area Chart, Bar Chart, Donut Chart |
28
+ | Element | Button, Button Group, Badge, Tag, Avatar, Alert, Banner, Card, Divider, Kbd, Copy Button, Command Block, Check List |
29
+ | Feedback | Spinner and Preloader, Loader, Skeleton, Progress, Circular Progress, Toast, Empty State, Error State |
30
+ | Form | Input, Number Input, Textarea, Code Editor, File Editor, Select, Combobox, Tag Input, CheckBox, CheckRadio, Radio Card, Switch, Switch Card, Search Bar, File Upload, Slider, Date Picker, Month Picker, Color Input, Time Picker, Form, Form Array, Key-Value Input, Taint Input, Theme Mode, Font Scale |
31
+ | Overlay | Modal, Delete Modal, Confirm Modal, Side Panel, Dropdown, Popover, Tooltip |
32
+ | Layout | Page, Sidebar, Navbar, Header, Product Hero, Content Table, Section Content, Settings Row, Accordion, Status Bar, Footer |
33
+ | Navigation | Breadcrumb, Tabs, Steps, Pagination, Locale Switcher, Side Tabs, App Switcher, Cluster Switcher, Notifications |
34
+ | Data | Table, Bulk Action Bar, Info Table, List, Cell Value |
35
+ | Cards | Stat Card, Feature Card, Resource Card, Usage Card, Cost Summary |
36
+ | Charts (`/charts`) | Line Chart, Area Chart, Bar Chart, Donut Chart, Map Chart, Graph Chart, Tree Chart |
36
37
 
37
38
  Example pages put them together: a database list, a database detail page, a create wizard, settings, empty and error states, and a full app shell.
38
39
 
@@ -42,14 +43,19 @@ The look and voice are the AppsCode Cloud console's (`cloud-ui`), so the system
42
43
 
43
44
  - **Type:** Roboto for the interface, Google Sans Flex for headings and JetBrains Mono for code and resource names, on a 14px base.
44
45
  - **Colour:** white cards on a `#f9fafb` page, neutral greys (`#252525` text, `#5c5c5c` descriptions, one `#ebebeb` border), the `#00a651` brand green, and success, info, warning, danger and pending statuses, each with an AA `-strong` text colour.
45
- - **Shape:** 40px controls with a 10px radius, 14px cards and 16px dialogs. Cards rest on a border; shadows mark only what floats.
46
+ - **Shape:** 42px buttons and fields with a 10px radius, 14px cards and 16px dialogs. Cards rest on a border; shadows mark only what floats.
46
47
  - **Focus:** a `focus-ring` utility (primary border and a 2px halo) on form fields, and a 2px offset ring on buttons.
47
48
  - **Dark mode:** class-based. Every token has a dark value, so components need no dark-specific code.
48
49
  - **Icons:** [Lucide](https://lucide.dev) (`@lucide/vue`). Components never draw their own SVG icons.
49
50
 
50
51
  ## Using it in an app
51
52
 
52
- The package is `@ac-design/design-system` **2.0.0**. `npm run build:lib` writes it to `dist/lib`, and `npm pack` makes an installable tarball.
53
+ The package is [`@ac-design/design-system`](https://www.npmjs.com/package/@ac-design/design-system) on npm. `npm run build:lib` writes it to `dist/lib`, and `npm pack` makes an installable tarball.
54
+
55
+ ```sh
56
+ npm install @ac-design/design-system @lucide/vue
57
+ npm install echarts # only if you use /charts
58
+ ```
53
59
 
54
60
  ## Styling rule
55
61
 
@@ -59,54 +65,50 @@ There are two ways to load the styles:
59
65
 
60
66
  | The app… | Load | Tailwind in the app |
61
67
  | --- | --- | --- |
62
- | still loads the old Bulma/SCSS design system (cluster-ui, kubedb-ui, platform-ui…) | `compat.css` | No |
63
- | doesn't load the old styles (new apps, or after migrating) | `theme.css` with Tailwind v4 | Yes |
64
-
65
- ### In an app that still loads the old styles
68
+ | runs Tailwind v4 (cloud-ui, cluster-ui, kubedb-ui, platform-ui and new apps) | `theme.css` | Yes |
69
+ | still loads the old Bulma/SCSS design system and has no Tailwind | `compat.css` | No |
66
70
 
67
- The docs page **Getting Started → Existing Apps** has the full guide. In short:
68
-
69
- ```sh
70
- # installed next to the old 2.x package, under a second name
71
- npm install @appscode/ds-next@npm:@ac-design/design-system@2.0.0 @lucide/vue
72
- ```
71
+ ### In an app with Tailwind v4
73
72
 
74
73
  ```ts
75
- // main.ts (Vite), or the `css` list in nuxt.config.ts: after the old styles
76
- import "./assets/styles/scss/main.scss";
74
+ // main.ts: fonts once
77
75
  import "@fontsource-variable/roboto";
78
76
  import "@fontsource-variable/google-sans-flex/opsz.css";
79
77
  import "@fontsource-variable/jetbrains-mono";
80
- import "@appscode/ds-next/compat.css";
81
78
  ```
82
79
 
83
- - **`compat.css`** is the library's styles, precompiled and fenced in: they apply only inside new components, and win over the old `!important` utilities there. Old pages don't change; this was checked pixel by pixel in kubedb-ui.
84
- - **Don't put utility classes on new components** while both are loaded. Wrap the component in your own element for spacing.
80
+ ```css
81
+ /* your main CSS file */
82
+ @import "tailwindcss";
83
+ @import "@ac-design/design-system/theme.css";
84
+ ```
85
+
86
+ ### In an app with only the old styles
85
87
 
86
- ### In an app without the old styles
88
+ The docs page **Getting Started → Existing Apps** has the full guide. In short:
87
89
 
88
90
  ```ts
89
- // main.ts: fonts once
91
+ // main.ts (Vite), or the `css` list in nuxt.config.ts: after the old styles
92
+ import "./assets/styles/scss/main.scss";
90
93
  import "@fontsource-variable/roboto";
91
94
  import "@fontsource-variable/google-sans-flex/opsz.css";
92
95
  import "@fontsource-variable/jetbrains-mono";
96
+ import "@ac-design/design-system/compat.css";
93
97
  ```
94
98
 
95
- ```css
96
- /* your main CSS file */
97
- @import "tailwindcss";
98
- @import "@ac-design/design-system/theme.css";
99
- ```
99
+ - **`compat.css`** is the library's styles, precompiled and fenced in: they apply only inside new components, and win over the old `!important` utilities there. Old pages don't change; this was checked pixel by pixel in kubedb-ui.
100
+ - **Don't put utility classes on new components** while both are loaded. Wrap the component in your own element for spacing.
100
101
 
101
102
  ### Either way
102
103
 
103
104
  ```ts
104
105
  import { AcButton, AcInput, useToast } from "@ac-design/design-system";
105
106
  import { AcCodeEditor, AcFileEditor } from "@ac-design/design-system/editor";
107
+ import { AcLineChart, AcDonutChart } from "@ac-design/design-system/charts";
106
108
  ```
107
109
 
108
110
  - **Requirements:** Vue 3.5+, Vite 5+ or Nuxt 3+, and TypeScript with `moduleResolution: "bundler"`. Every component renders on the server, so Nuxt SSR works.
109
- - **Peer dependencies:** `vue` ^3.5 and `@lucide/vue` ^1.0. CodeMirror, Ajv and `yaml` are regular dependencies, bundled only by apps that import from `/editor`.
111
+ - **Peer dependencies:** `vue` ^3.5 and `@lucide/vue` ^1.0, plus `echarts` ^6 (optional) for `/charts`. CodeMirror, Ajv and `yaml` are regular dependencies, bundled only by apps that import from `/editor`.
110
112
  - **Dark mode:** put `.dark` on `<html>` (the old `.is-dark-theme` class works too), or call `useColorMode().setMode("dark" | "light" | "system")`. The user's choice is saved under the old `themeMode` localStorage key.
111
113
  - **Toasts:** mount `<AcToaster />` once near the root, then call `useToast().success("Saved")` anywhere.
112
114
  - **Brand colour:** `useBrandColor().setColor("#0066cc")` re-hues the primary scale. The old `HexToHSL`, `setThemeHSL` and `getThemeHSL` helpers are exported under the same names.
@@ -118,6 +120,7 @@ import { AcCodeEditor, AcFileEditor } from "@ac-design/design-system/editor";
118
120
  | --- | --- |
119
121
  | `npm run dev` | Regenerates the API data, then starts the docs site at http://localhost:5173 |
120
122
  | `npm run build` | Regenerates the API data, type-checks, and builds the docs site into `dist/` |
123
+ | `npm run preview` | Serves the built docs site from `dist/` |
121
124
  | `npm run build:lib` | Builds the library into `dist/lib`: ES modules, `.d.ts` files, `theme.css` and `compat.css` |
122
125
  | `npm run type-check` | `vue-tsc` over the whole project |
123
126
  | `npm run meta` | Regenerates `src/docs/generated/component-meta.json` (the API tables) from the components' types |
@@ -135,7 +138,8 @@ src/
135
138
  index.ts component exports, sorted
136
139
  types.ts shared types (Tone, Variant, Option, SelectOption)
137
140
  editor/ the /editor entry: index.ts, CodeMirror theme, YAML/JSON Schema checks
138
- composables/ useColorMode.ts, useToast.ts, useBrandColor.ts
141
+ charts/ the /charts entry: index.ts, ECharts setup, ChartFrame.vue (legend, tooltip, sr-only table)
142
+ composables/ useColorMode, useFontScale, useToast, useBrandColor, pagination, selection and form helpers
139
143
  docs/ the documentation site
140
144
  nav.ts every page: path, title, description, section, group, component
141
145
  router.ts hash router built from nav.ts
@@ -151,7 +155,7 @@ scripts/
151
155
  build-compat.mjs builds compat.css for apps that still load the old styles
152
156
  public/ favicon, logos
153
157
  vite.config.ts docs site
154
- vite.lib.config.ts library build (two entries: index and editor/index)
158
+ vite.lib.config.ts library build (three entries: index, editor/index and charts/index)
155
159
  ```
156
160
 
157
161
  ## Contributing
@@ -5415,6 +5415,9 @@ button:is([data-ac-ds], [data-ac-ds] *), input:where([type="button"], [type="res
5415
5415
  font-size: var(--text-xs) !important;
5416
5416
  line-height: var(--tw-leading, var(--text-xs--line-height)) !important;
5417
5417
  }
5418
+ .max-sm\:has-\[\>\.w-full\]\:basis-full:has( > .w-full):where([data-ac-ds], [data-ac-ds] *) {
5419
+ flex-basis: 100% !important;
5420
+ }
5418
5421
  }
5419
5422
  @media (width >= 40rem) {
5420
5423
  .sm\:inset-x-auto:where([data-ac-ds], [data-ac-ds] *) {
@@ -12,7 +12,7 @@ var g = {
12
12
  class: "mt-0.5 text-xs leading-4 text-muted"
13
13
  }, b = {
14
14
  key: 1,
15
- class: "flex max-w-full min-w-0 shrink-0 flex-wrap items-center gap-2 sm:flex-nowrap"
15
+ class: "flex max-w-full min-w-0 shrink-0 flex-wrap items-center gap-2 max-sm:has-[>.w-full]:basis-full sm:flex-nowrap"
16
16
  }, x = /*@__PURE__*/ c({
17
17
  __name: "AcCard",
18
18
  props: {
@@ -4,6 +4,8 @@ export interface Props {
4
4
  defaultScale?: FontScale | number;
5
5
  /** `icons` is a compact switch of four "A"s; `labels` adds the words, for menus and settings pages. */
6
6
  display?: "icons" | "labels";
7
+ /** Fills its row and shares the width between the buttons, e.g. in a 256px menu where the labelled switch would overflow. */
8
+ block?: boolean;
7
9
  }
8
10
  declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
11
  "set:scale": (scale: number) => any;
@@ -11,6 +13,7 @@ declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {},
11
13
  "onSet:scale"?: ((scale: number) => any) | undefined;
12
14
  }>, {
13
15
  display: "icons" | "labels";
16
+ block: boolean;
14
17
  defaultScale: FontScale | number;
15
18
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
19
  declare const _default: typeof __VLS_export;
@@ -1,26 +1,24 @@
1
1
  import { FONT_SCALES as e, useFontScale as t } from "../composables/useFontScale.js";
2
2
  import { Fragment as n, createElementBlock as r, createElementVNode as i, defineComponent as a, normalizeClass as o, openBlock as s, renderList as c, toDisplayString as l, unref as u } from "vue";
3
3
  //#region src/lib/components/AcFontScale.vue?vue&type=script&setup=true&lang.ts
4
- var d = {
5
- role: "radiogroup",
6
- "aria-label": "Text size",
7
- class: "inline-flex items-center gap-1 rounded-10 bg-surface-sunken p-1",
8
- "data-ac-ds": "",
9
- "data-testid": "ac-font-scale"
10
- }, f = [
4
+ var d = [
11
5
  "aria-checked",
12
6
  "aria-label",
13
7
  "title",
14
8
  "onClick"
15
- ], p = { key: 1 }, m = /*@__PURE__*/ a({
9
+ ], f = { key: 1 }, p = /*@__PURE__*/ a({
16
10
  __name: "AcFontScale",
17
11
  props: {
18
12
  defaultScale: { default: "default" },
19
- display: { default: "icons" }
13
+ display: { default: "icons" },
14
+ block: {
15
+ type: Boolean,
16
+ default: !1
17
+ }
20
18
  },
21
19
  emits: ["set:scale"],
22
- setup(a, { emit: m }) {
23
- let h = a, g = m, _ = [
20
+ setup(a, { emit: p }) {
21
+ let m = a, h = p, g = [
24
22
  {
25
23
  value: "small",
26
24
  label: "Small",
@@ -41,25 +39,31 @@ var d = {
41
39
  label: "Larger",
42
40
  glyph: "text-2xl"
43
41
  }
44
- ], { scale: v, preset: y, setScale: b } = t({ defaultScale: typeof h.defaultScale == "number" ? h.defaultScale : e[h.defaultScale] });
45
- function x(e) {
46
- b(e), g("set:scale", v.value);
42
+ ], { scale: _, preset: v, setScale: y } = t({ defaultScale: typeof m.defaultScale == "number" ? m.defaultScale : e[m.defaultScale] });
43
+ function b(e) {
44
+ y(e), h("set:scale", _.value);
47
45
  }
48
- return (e, t) => (s(), r("div", d, [(s(), r(n, null, c(_, (e) => i("button", {
46
+ return (e, t) => (s(), r("div", {
47
+ role: "radiogroup",
48
+ "aria-label": "Text size",
49
+ class: o(["items-center gap-1 rounded-10 bg-surface-sunken p-1", a.block ? "flex w-full" : "inline-flex"]),
50
+ "data-ac-ds": "",
51
+ "data-testid": "ac-font-scale"
52
+ }, [(s(), r(n, null, c(g, (e) => i("button", {
49
53
  key: e.value,
50
54
  type: "button",
51
55
  role: "radio",
52
- "aria-checked": u(y) === e.value,
56
+ "aria-checked": u(v) === e.value,
53
57
  "aria-label": a.display === "icons" ? `${e.label} text` : void 0,
54
58
  title: a.display === "icons" ? `${e.label} text` : void 0,
55
- class: o(["inline-flex h-7 cursor-pointer items-center justify-center gap-1.5 rounded-8 text-base font-medium transition-[background-color,color,box-shadow] duration-200 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset focus-visible:outline-none", [a.display === "icons" ? "w-7" : "px-3", u(y) === e.value ? "bg-surface text-heading shadow-sm" : "text-muted hover:text-heading"]]),
56
- onClick: (t) => x(e.value)
59
+ class: o(["inline-flex h-7 cursor-pointer items-center justify-center gap-1.5 rounded-8 text-base font-medium transition-[background-color,color,box-shadow] duration-200 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset focus-visible:outline-none", [a.display === "icons" ? "w-7" : a.block ? "flex-1 px-1" : "px-3", u(v) === e.value ? "bg-surface text-heading shadow-sm" : "text-muted hover:text-heading"]]),
60
+ onClick: (t) => b(e.value)
57
61
  }, [a.display === "icons" ? (s(), r("span", {
58
62
  key: 0,
59
63
  class: o(["leading-none font-semibold", e.glyph]),
60
64
  "aria-hidden": "true"
61
- }, "A", 2)) : (s(), r("span", p, l(e.label), 1))], 10, f)), 64))]));
65
+ }, "A", 2)) : (s(), r("span", f, l(e.label), 1))], 10, d)), 64))], 2));
62
66
  }
63
67
  });
64
68
  //#endregion
65
- export { m as default };
69
+ export { p as default };
@@ -12,7 +12,7 @@ var _e = ["aria-describedby"], ve = { class: "flex min-h-8 items-center justify-
12
12
  }, be = {
13
13
  key: 2,
14
14
  class: "sr-only"
15
- }, xe = { class: "ac-scrollbar" }, Se = { class: "w-full border-separate border-spacing-0 text-base" }, Ce = { class: "sr-only" }, we = {
15
+ }, xe = { class: "ac-scrollbar relative" }, Se = { class: "w-full border-separate border-spacing-0 text-base" }, Ce = { class: "sr-only" }, we = {
16
16
  key: 0,
17
17
  scope: "col",
18
18
  class: "h-10 w-px border-b border-border bg-surface-sunken/50 px-3 text-right"
@@ -7,6 +7,8 @@ export interface Props {
7
7
  collapsible?: boolean;
8
8
  /** Keeps the sidebar inside its parent instead of the viewport: the mobile drawer and backdrop use `absolute`, and the mobile layout follows the parent's width. Give the parent `relative`, `overflow-hidden` and a height. For previews and embedded shells. */
9
9
  contained?: boolean;
10
+ /** Narrows the docked sidebar from 256px to 208px between 1024px and 1279px, as cloud-ui. Set `false` to keep 256px when labels are long. */
11
+ shrink?: boolean;
10
12
  /** Width in px below which the sidebar becomes an off-canvas drawer: the window's width, or the parent's with `contained`. `0` keeps it docked. */
11
13
  breakpoint?: number;
12
14
  /** With the sidebar collapsed to the rail, opens it to full width over the page while the pointer or keyboard focus is inside, then folds it back. The page doesn't shift. */
@@ -61,6 +63,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
61
63
  top: string;
62
64
  dark: boolean;
63
65
  contained: boolean;
66
+ shrink: boolean;
64
67
  breakpoint: number;
65
68
  hoverExpand: boolean;
66
69
  footerScrolls: boolean;
@@ -35,6 +35,10 @@ var E = [
35
35
  type: Boolean,
36
36
  default: !1
37
37
  },
38
+ shrink: {
39
+ type: Boolean,
40
+ default: !0
41
+ },
38
42
  breakpoint: { default: 768 },
39
43
  hoverExpand: {
40
44
  type: Boolean,
@@ -81,17 +85,17 @@ var E = [
81
85
  top: k.top,
82
86
  height: `calc(100dvh - ${k.top})`
83
87
  }), _e = n(() => {
84
- let e = k.dark ? "dark border-r border-border bg-sidebar text-body shadow-sm" : "border-r border-border bg-sidebar-panel/95 text-body shadow-sm backdrop-blur-sm";
88
+ let e = k.dark ? "dark border-r border-border bg-sidebar text-body" : "border-r border-border bg-sidebar-panel/95 text-body backdrop-blur-sm";
85
89
  return F.value ? [
86
90
  e,
87
91
  k.contained ? "absolute" : "fixed",
88
- "inset-y-0 left-0 z-[80] w-64 duration-300 ease-out-soft motion-reduce:transition-none",
92
+ "inset-y-0 left-0 z-[80] w-64 shadow-sm duration-300 ease-out-soft motion-reduce:transition-none",
89
93
  j.value ? "visible translate-x-0 transition-[translate]" : "invisible -translate-x-full transition-[translate,visibility]"
90
94
  ] : [
91
95
  e,
92
96
  B.value ? [k.contained ? "absolute inset-y-0 left-0" : "fixed left-0", "z-30 shadow-xl"] : k.contained ? "relative h-full" : k.fixed ? "fixed left-0 z-30" : "sticky",
93
97
  "transition-[width] duration-200 ease-out-soft motion-reduce:transition-none",
94
- V.value ? "w-14" : k.contained ? "w-64" : "w-64 lg:w-52 xl:w-64"
98
+ V.value ? "w-14" : k.contained || !k.shrink ? "w-64" : "w-64 lg:w-52 xl:w-64"
95
99
  ];
96
100
  });
97
101
  function J() {
@@ -4,7 +4,7 @@ import n from "./AcPagination.js";
4
4
  import { Fragment as r, computed as i, createBlock as a, createCommentVNode as o, createElementBlock as s, createElementVNode as c, createTextVNode as l, createVNode as u, defineComponent as d, getCurrentInstance as f, mergeModels as p, mergeProps as ee, normalizeClass as m, normalizeStyle as h, openBlock as g, renderList as _, renderSlot as v, resolveDynamicComponent as y, toDisplayString as b, unref as x, useId as te, useModel as S, useSlots as ne, watch as re, withCtx as C, withModifiers as w } from "vue";
5
5
  import { Check as T, ChevronDown as ie, ChevronRight as ae, ChevronUp as oe, Minus as se } from "@lucide/vue";
6
6
  //#region src/lib/components/AcTable.vue?vue&type=script&setup=true&lang.ts
7
- var ce = { class: "ac-scrollbar" }, le = { class: "w-full border-separate border-spacing-0 text-base" }, ue = { class: "relative flex size-[18px]" }, de = [
7
+ var ce = { class: "ac-scrollbar relative" }, le = { class: "w-full border-separate border-spacing-0 text-base" }, ue = { class: "relative flex size-[18px]" }, de = [
8
8
  "checked",
9
9
  "indeterminate",
10
10
  "disabled"
@@ -4,6 +4,8 @@ export interface Props {
4
4
  defaultMode?: ColorMode;
5
5
  /** `icons` is a compact 3-icon switch; `labels` adds the words, for menus and settings pages; `toggle` is cloud-ui's sun/moon switch for the navbar (64×32px), light or dark only. */
6
6
  display?: "icons" | "labels" | "toggle";
7
+ /** Fills its row and shares the width between the buttons, e.g. in a 256px menu where the labelled switch would overflow. Not for `toggle`. */
8
+ block?: boolean;
7
9
  }
8
10
  type __VLS_Props = Props;
9
11
  type __VLS_ModelProps = {
@@ -19,6 +21,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
19
21
  "onUpdate:dark"?: ((value: boolean | undefined) => any) | undefined;
20
22
  }>, {
21
23
  display: "icons" | "labels" | "toggle";
24
+ block: boolean;
22
25
  defaultMode: ColorMode;
23
26
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
24
27
  declare const _default: typeof __VLS_export;
@@ -2,23 +2,20 @@ import { useColorMode as e } from "../composables/useColorMode.js";
2
2
  import { Fragment as t, computed as n, createBlock as r, createCommentVNode as i, createElementBlock as a, createElementVNode as o, createVNode as s, defineComponent as c, mergeModels as l, normalizeClass as u, onBeforeUnmount as d, openBlock as f, ref as p, renderList as m, resolveDynamicComponent as h, toDisplayString as g, unref as _, useModel as v, watch as y } from "vue";
3
3
  import { Monitor as b, Moon as x, Sun as S } from "@lucide/vue";
4
4
  //#region src/lib/components/AcThemeMode.vue?vue&type=script&setup=true&lang.ts
5
- var C = ["aria-checked", "title"], w = {
6
- key: 1,
7
- role: "radiogroup",
8
- "aria-label": "Theme",
9
- class: "inline-flex items-center gap-1 rounded-10 bg-surface-sunken p-1",
10
- "data-ac-ds": "",
11
- "data-testid": "ac-theme-mode"
12
- }, T = [
5
+ var C = ["aria-checked", "title"], w = [
13
6
  "aria-checked",
14
7
  "aria-label",
15
8
  "title",
16
9
  "onClick"
17
- ], E = { key: 0 }, D = /*@__PURE__*/ c({
10
+ ], T = { key: 0 }, E = /*@__PURE__*/ c({
18
11
  __name: "AcThemeMode",
19
12
  props: /*@__PURE__*/ l({
20
13
  defaultMode: { default: "light" },
21
- display: { default: "icons" }
14
+ display: { default: "icons" },
15
+ block: {
16
+ type: Boolean,
17
+ default: !1
18
+ }
22
19
  }, {
23
20
  dark: {
24
21
  type: Boolean,
@@ -28,7 +25,7 @@ var C = ["aria-checked", "title"], w = {
28
25
  }),
29
26
  emits: /*@__PURE__*/ l(["set:theme"], ["update:dark"]),
30
27
  setup(c, { emit: l }) {
31
- let D = c, O = v(c, "dark"), k = l, A = [
28
+ let E = c, D = v(c, "dark"), O = l, k = [
32
29
  {
33
30
  value: "light",
34
31
  label: "Light",
@@ -44,60 +41,67 @@ var C = ["aria-checked", "title"], w = {
44
41
  label: "System",
45
42
  icon: b
46
43
  }
47
- ], j = O.value === void 0 ? e({ defaultMode: D.defaultMode }) : null, M = n(() => j ? j.isDark.value : !!O.value), N = n(() => j ? j.mode.value : O.value ? "dark" : "light"), P = p(M.value), F;
48
- function I(e) {
49
- if (j) {
50
- j.setMode(e), k("set:theme", M.value ? "dark" : "light");
44
+ ], A = D.value === void 0 ? e({ defaultMode: E.defaultMode }) : null, j = n(() => A ? A.isDark.value : !!D.value), M = n(() => A ? A.mode.value : D.value ? "dark" : "light"), N = p(j.value), P;
45
+ function F(e) {
46
+ if (A) {
47
+ A.setMode(e), O("set:theme", j.value ? "dark" : "light");
51
48
  return;
52
49
  }
53
50
  let t = e === "system" ? window.matchMedia("(prefers-color-scheme: dark)").matches : e === "dark";
54
- O.value = t, k("set:theme", t ? "dark" : "light");
51
+ D.value = t, O("set:theme", t ? "dark" : "light");
55
52
  }
56
- function L() {
57
- P.value = !P.value, clearTimeout(F), F = setTimeout(() => {
58
- F = void 0, I(P.value ? "dark" : "light");
53
+ function I() {
54
+ N.value = !N.value, clearTimeout(P), P = setTimeout(() => {
55
+ P = void 0, F(N.value ? "dark" : "light");
59
56
  }, 200);
60
57
  }
61
- return y(M, (e) => {
62
- F === void 0 && (P.value = e);
63
- }), d(() => clearTimeout(F)), (e, n) => c.display === "toggle" ? (f(), a("button", {
58
+ return y(j, (e) => {
59
+ P === void 0 && (N.value = e);
60
+ }), d(() => clearTimeout(P)), (e, n) => c.display === "toggle" ? (f(), a("button", {
64
61
  key: 0,
65
62
  type: "button",
66
63
  role: "switch",
67
- "aria-checked": P.value,
64
+ "aria-checked": N.value,
68
65
  "aria-label": "Dark mode",
69
- title: P.value ? "Switch to light mode" : "Switch to dark mode",
66
+ title: N.value ? "Switch to light mode" : "Switch to dark mode",
70
67
  class: "relative inline-flex h-8 w-16 shrink-0 cursor-pointer items-center justify-center rounded-full border border-border focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface focus-visible:outline-none",
71
68
  "data-ac-ds": "",
72
69
  "data-testid": "ac-theme-mode",
73
- onClick: L
70
+ onClick: I
74
71
  }, [
75
72
  o("span", {
76
- class: u(["inline-flex transition-[translate,rotate,opacity] duration-500 ease-[cubic-bezier(0.26,2,0.46,0.71)] motion-reduce:transition-none", P.value ? "translate-x-[10px] rotate-0 opacity-0" : "translate-x-[3px] rotate-[15deg] opacity-100"]),
73
+ class: u(["inline-flex transition-[translate,rotate,opacity] duration-500 ease-[cubic-bezier(0.26,2,0.46,0.71)] motion-reduce:transition-none", N.value ? "translate-x-[10px] rotate-0 opacity-0" : "translate-x-[3px] rotate-[15deg] opacity-100"]),
77
74
  "aria-hidden": "true"
78
75
  }, [s(_(S), { class: "size-[19px] text-theme-sun" })], 2),
79
76
  o("span", {
80
- class: u(["size-[21px] shrink-0 rounded-full inset-ring-2 transition-[translate,background-color] duration-500 ease-[cubic-bezier(0.26,2,0.46,0.71)] motion-reduce:transition-none", P.value ? "-translate-x-[14px] bg-theme-moon inset-ring-white" : "translate-x-4 bg-theme-sun-soft inset-ring-theme-sun"]),
77
+ class: u(["size-[21px] shrink-0 rounded-full inset-ring-2 transition-[translate,background-color] duration-500 ease-[cubic-bezier(0.26,2,0.46,0.71)] motion-reduce:transition-none", N.value ? "-translate-x-[14px] bg-theme-moon inset-ring-white" : "translate-x-4 bg-theme-sun-soft inset-ring-theme-sun"]),
81
78
  "aria-hidden": "true"
82
79
  }, null, 2),
83
80
  o("span", {
84
- class: u(["inline-flex transition-[translate,rotate,opacity] duration-500 ease-[cubic-bezier(0.26,2.5,0.46,0.71)] motion-reduce:transition-none", P.value ? "-translate-x-[3px] -rotate-[15deg] opacity-100" : "-translate-x-2 rotate-0 opacity-0"]),
81
+ class: u(["inline-flex transition-[translate,rotate,opacity] duration-500 ease-[cubic-bezier(0.26,2.5,0.46,0.71)] motion-reduce:transition-none", N.value ? "-translate-x-[3px] -rotate-[15deg] opacity-100" : "-translate-x-2 rotate-0 opacity-0"]),
85
82
  "aria-hidden": "true"
86
83
  }, [s(_(x), { class: "size-[21px] text-heading" })], 2)
87
- ], 8, C)) : (f(), a("div", w, [(f(), a(t, null, m(A, (e) => o("button", {
84
+ ], 8, C)) : (f(), a("div", {
85
+ key: 1,
86
+ role: "radiogroup",
87
+ "aria-label": "Theme",
88
+ class: u(["items-center gap-1 rounded-10 bg-surface-sunken p-1", c.block ? "flex w-full" : "inline-flex"]),
89
+ "data-ac-ds": "",
90
+ "data-testid": "ac-theme-mode"
91
+ }, [(f(), a(t, null, m(k, (e) => o("button", {
88
92
  key: e.value,
89
93
  type: "button",
90
94
  role: "radio",
91
- "aria-checked": N.value === e.value,
95
+ "aria-checked": M.value === e.value,
92
96
  "aria-label": c.display === "icons" ? `${e.label} theme` : void 0,
93
97
  title: c.display === "icons" ? `${e.label} theme` : void 0,
94
- class: u(["inline-flex h-7 cursor-pointer items-center justify-center gap-1.5 rounded-8 text-base font-medium transition-[background-color,color,box-shadow] duration-200 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset focus-visible:outline-none", [c.display === "icons" ? "w-7" : "px-3", N.value === e.value ? "bg-surface text-heading shadow-sm" : "text-muted hover:text-heading"]]),
95
- onClick: (t) => I(e.value)
98
+ class: u(["inline-flex h-7 cursor-pointer items-center justify-center gap-1.5 rounded-8 text-base font-medium transition-[background-color,color,box-shadow] duration-200 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset focus-visible:outline-none", [c.display === "icons" ? "w-7" : c.block ? "flex-1 px-1" : "px-3", M.value === e.value ? "bg-surface text-heading shadow-sm" : "text-muted hover:text-heading"]]),
99
+ onClick: (t) => F(e.value)
96
100
  }, [(f(), r(h(e.icon), {
97
101
  class: "size-4",
98
102
  "aria-hidden": "true"
99
- })), c.display === "labels" ? (f(), a("span", E, g(e.label), 1)) : i("", !0)], 10, T)), 64))]));
103
+ })), c.display === "labels" ? (f(), a("span", T, g(e.label), 1)) : i("", !0)], 10, w)), 64))], 2));
100
104
  }
101
105
  });
102
106
  //#endregion
103
- export { D as default };
107
+ export { E as default };
@@ -360,6 +360,7 @@ var me = {
360
360
  class: "mb-2 px-2 text-sm font-semibold tracking-[0.08em] text-muted uppercase"
361
361
  }, "Theme", 8, Ye), f(r, {
362
362
  display: "labels",
363
+ block: "",
363
364
  "onSet:theme": o[3] ||= (e) => k("set:theme", e)
364
365
  })], 8, Je)) : c("", !0),
365
366
  p.showFontScale ? (g(), l("div", {
@@ -370,7 +371,10 @@ var me = {
370
371
  }, [u("p", {
371
372
  id: `${S(M)}-scale`,
372
373
  class: "mb-2 px-2 text-sm font-semibold tracking-[0.08em] text-muted uppercase"
373
- }, "Text size", 8, Ze), f(n, { display: "labels" })], 8, Xe)) : c("", !0),
374
+ }, "Text size", 8, Ze), f(n, {
375
+ display: "labels",
376
+ block: ""
377
+ })], 8, Xe)) : c("", !0),
374
378
  u("div", Qe, [
375
379
  (g(!0), l(i, null, v(p.items, (e) => (g(), s(b(q(e)), ne({ key: e.label }, { ref_for: !0 }, st(e), {
376
380
  role: "menuitem",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ac-design/design-system",
3
- "version": "2.10.3",
3
+ "version": "2.11.0",
4
4
  "description": "AppsCode design system: Tailwind CSS v4 theme and Vue 3 components.",
5
5
  "repository": {
6
6
  "type": "git",