webcake-ui-kit 1.0.0 → 1.0.2

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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +358 -8
  3. package/package.json +68 -5
  4. package/src/components/accordion/Accordion.vue +70 -0
  5. package/src/components/accordion/accordion.css +5 -0
  6. package/src/components/accordion-item/AccordionItem.vue +98 -0
  7. package/src/components/accordion-item/accordion-item.css +143 -0
  8. package/src/components/alert-dialog/AlertDialog.vue +82 -0
  9. package/src/components/alert-dialog/alert-dialog.css +33 -0
  10. package/src/components/badge/Badge.vue +2 -2
  11. package/src/components/badge/badge.css +1 -4
  12. package/src/components/breadcrumb/Breadcrumb.vue +85 -0
  13. package/src/components/breadcrumb/breadcrumb.css +90 -0
  14. package/src/components/button/Button.vue +77 -10
  15. package/src/components/button/button.css +258 -24
  16. package/src/components/button-group/ButtonGroup.vue +25 -0
  17. package/src/components/button-group/button-group.css +30 -0
  18. package/src/components/checkbox/Checkbox.vue +55 -0
  19. package/src/components/checkbox/checkbox.css +86 -0
  20. package/src/components/checkbox-group/CheckboxGroup.vue +50 -0
  21. package/src/components/checkbox-group/checkbox-group.css +35 -0
  22. package/src/components/dialog/Dialog.vue +355 -0
  23. package/src/components/dialog/dialog.css +255 -0
  24. package/src/components/divider/Divider.vue +35 -0
  25. package/src/components/divider/divider.css +38 -0
  26. package/src/components/input/Input.vue +99 -0
  27. package/src/components/input/input.css +123 -0
  28. package/src/components/pagination/Pagination.vue +211 -0
  29. package/src/components/pagination/pagination.css +13 -0
  30. package/src/components/radio/Radio.vue +74 -0
  31. package/src/components/radio/radio.css +89 -0
  32. package/src/components/radio-group/RadioGroup.vue +70 -0
  33. package/src/components/radio-group/radio_group.css +11 -0
  34. package/src/components/rich-checkbox-group/RichCheckboxGroup.vue +59 -0
  35. package/src/components/rich-checkbox-group/rich-checkbox-group.css +54 -0
  36. package/src/components/rich-switch-group/RichSwitchGroup.vue +49 -0
  37. package/src/components/rich-switch-group/rich_switch_group.css +45 -0
  38. package/src/components/select/Select.vue +262 -0
  39. package/src/components/select/select.css +207 -0
  40. package/src/components/select-option/SelectOption.vue +82 -0
  41. package/src/components/select-option/select_option.css +60 -0
  42. package/src/components/sidebar-group-label/SidebarGroupLabel.vue +68 -0
  43. package/src/components/sidebar-group-label/sidebar_group_label.css +61 -0
  44. package/src/components/sidebar-item/SidebarItem.vue +110 -0
  45. package/src/components/sidebar-item/sidebar_item.css +142 -0
  46. package/src/components/slider/Slider.vue +255 -0
  47. package/src/components/slider/slider.css +89 -0
  48. package/src/components/spinner/Spinner.vue +47 -0
  49. package/src/components/spinner/spinner.css +48 -0
  50. package/src/components/switch/Switch.vue +32 -0
  51. package/src/components/switch/switch.css +46 -0
  52. package/src/components/switch-group/SwitchGroup.vue +32 -0
  53. package/src/components/switch-group/switch_group.css +28 -0
  54. package/src/components/tabs/Tabs.vue +57 -0
  55. package/src/components/tabs/tabs.css +118 -0
  56. package/src/components/tag/Tag.vue +47 -0
  57. package/src/components/tag/tag.css +115 -0
  58. package/src/components/toggle/Toggle.vue +112 -0
  59. package/src/components/toggle/toggle.css +174 -0
  60. package/src/components/toggle-group/ToggleGroup.vue +57 -0
  61. package/src/components/toggle-group/toggle-group.css +68 -0
  62. package/src/icons/LoaderIcon.vue +22 -0
  63. package/src/index.js +29 -2
  64. package/src/styles/.omc/state/agent-replay-645326b7-372b-463d-ab45-0adaafe31a51.jsonl +2 -0
  65. package/src/styles/.omc/state/subagent-tracking.json +7 -0
  66. package/src/styles/alpha_colors.css +35 -37
  67. package/src/styles/border_radius.css +21 -23
  68. package/src/styles/brand_colors.css +37 -39
  69. package/src/styles/chart_colors.css +27 -29
  70. package/src/styles/color_general.css +206 -201
  71. package/src/styles/raw_colors.css +267 -269
  72. package/src/styles/shadow.css +10 -12
  73. package/src/styles/spacing.css +31 -33
  74. package/src/styles/typography.css +76 -78
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Webcake Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,12 +1,362 @@
1
- # webcake-ui-kit
1
+ <div align="center">
2
2
 
3
- ### Requirement
3
+ # 🍰 webcake-ui-kit
4
4
 
5
- - Node 14
6
- - Python <= 3.10
5
+ ### The Vue UI kit that doesn't make you choose.
7
6
 
8
- ### Development
7
+ **One library. Two Vue versions. Zero build step.**
8
+ Ship the same components to Vue 2.7 _and_ Vue 3 — from a single source, with one import.
9
9
 
10
- - Playground Vue2: `cd playground-vue2 && npm i`
11
- - Storybook Vue3: `cd storybook-vue3 && npm i`
12
- - At root: `npm run dev:vue2` | `npm run dev:vue3`
10
+ <br />
11
+
12
+ [![npm version](https://img.shields.io/npm/v/webcake-ui-kit.svg?style=for-the-badge&color=ff6b9d&logo=npm&logoColor=white)](https://www.npmjs.com/package/webcake-ui-kit)
13
+ [![Vue 2 & 3](https://img.shields.io/badge/Vue-2.7%20%7C%C2%A03.x-42b883?style=for-the-badge&logo=vue.js&logoColor=white)](https://vuejs.org/)
14
+ [![No build](https://img.shields.io/badge/build-not%20required-success?style=for-the-badge)](#-why-ship-raw-sfc)
15
+ [![License](https://img.shields.io/badge/license-ISC-blue?style=for-the-badge)](LICENSE)
16
+
17
+ <br />
18
+
19
+ ### 📖 **[Live Docs & Storybook → ui.webcake.io](https://ui.webcake.io)**
20
+
21
+ <sub>Browse every component, prop, slot, and variant — running live in the browser.</sub>
22
+
23
+ <br />
24
+
25
+ </div>
26
+
27
+ ---
28
+
29
+ ## 👋 Welcome
30
+
31
+ Migrating from Vue 2 to Vue 3 is painful enough — your UI library shouldn't make it worse.
32
+
33
+ Most Vue component libraries force you to **pick a side**. Choose Vue 2 → you're stuck. Choose Vue 3 → you have to rewrite the app first. Either way, you carry the cost.
34
+
35
+ **webcake-ui-kit refuses that tradeoff.** Every component is hand-authored under strict dual-compatibility rules so the _same import_ compiles, renders, and behaves identically on **Vue 2.7** and **Vue 3.4+** — from a codebase you can grep, fork, and theme as if it were your own.
36
+
37
+ ```js
38
+ import { Button, Dialog, Input } from 'webcake-ui-kit'
39
+ // ✅ Vue 2.7 — works
40
+ // ✅ Vue 3.x — works
41
+ // ✅ Same API. Same styles. Same behavior. One source of truth.
42
+ ```
43
+
44
+ No `-vue2` package. No `-vue3` package. No build artifact gymnastics. Just `.vue` files, the way the framework intended.
45
+
46
+ ---
47
+
48
+ ## ⚡ Quick start
49
+
50
+ Get a button on screen in **under 60 seconds**.
51
+
52
+ ### 1. Install
53
+
54
+ ```bash
55
+ npm install webcake-ui-kit
56
+ ```
57
+
58
+ ```bash
59
+ pnpm add webcake-ui-kit
60
+ ```
61
+
62
+ ```bash
63
+ yarn add webcake-ui-kit
64
+ ```
65
+
66
+ > Peer dependency: `vue ^2.6.0 || ^3.0.0`. That's it.
67
+
68
+ ### 2. Import the styles once
69
+
70
+ ```js
71
+ import 'webcake-ui-kit/styles'
72
+ ```
73
+
74
+ ### 3. Register components
75
+
76
+ <table>
77
+ <tr>
78
+ <th width="50%">
79
+
80
+ **Vue 3**
81
+
82
+ </th>
83
+ <th width="50%">
84
+
85
+ **Vue 2.7**
86
+
87
+ </th>
88
+ </tr>
89
+ <tr>
90
+ <td>
91
+
92
+ ```js
93
+ // main.js
94
+ import { createApp } from 'vue'
95
+ import { Button, Dialog, Input } from 'webcake-ui-kit'
96
+ import 'webcake-ui-kit/styles'
97
+ import App from './App.vue'
98
+
99
+ const app = createApp(App)
100
+ app.component('Button', Button)
101
+ app.component('Dialog', Dialog)
102
+ app.component('Input', Input)
103
+ app.mount('#app')
104
+ ```
105
+
106
+ </td>
107
+ <td>
108
+
109
+ ```js
110
+ // main.js
111
+ import Vue from 'vue'
112
+ import { Button, Dialog, Input } from 'webcake-ui-kit'
113
+ import 'webcake-ui-kit/styles'
114
+ import App from './App.vue'
115
+
116
+ Vue.component('Button', Button)
117
+ Vue.component('Dialog', Dialog)
118
+ Vue.component('Input', Input)
119
+
120
+ new Vue({ render: h => h(App) }).$mount('#app')
121
+ ```
122
+
123
+ </td>
124
+ </tr>
125
+ </table>
126
+
127
+ ### 4. Use it
128
+
129
+ ```vue
130
+ <template>
131
+ <div>
132
+ <Button variant="primary" @click="open = true">Open dialog</Button>
133
+
134
+ <Dialog v-model="open" title="Hello from webcake 👋">
135
+ <Input v-model="name" placeholder="Your name" />
136
+ </Dialog>
137
+ </div>
138
+ </template>
139
+
140
+ <script>
141
+ export default {
142
+ data: () => ({ open: false, name: '' })
143
+ }
144
+ </script>
145
+ ```
146
+
147
+ 🎉 **That's it.** The same `.vue` file works on both runtimes — no `#ifdef`, no wrapper, no shim.
148
+
149
+ ---
150
+
151
+ ## ✨ Why teams pick webcake
152
+
153
+ <table>
154
+ <tr>
155
+ <td width="33%" valign="top">
156
+
157
+ ### 🎯 True dual-compat
158
+
159
+ One source. Vue 2.7 and Vue 3.x. Tested on both runtimes in CI — every PR, every component.
160
+
161
+ </td>
162
+ <td width="33%" valign="top">
163
+
164
+ ### 📦 Ships raw SFC
165
+
166
+ No pre-compiled artifacts. Your bundler compiles `.vue` files once, alongside your app. Zero version lock.
167
+
168
+ </td>
169
+ <td width="33%" valign="top">
170
+
171
+ ### 🎨 Token-driven theming
172
+
173
+ Override `--color-primary` and the whole kit rebrands. No SASS, no providers, no JS theme objects.
174
+
175
+ </td>
176
+ </tr>
177
+ <tr>
178
+ <td valign="top">
179
+
180
+ ### ♿ Accessible by default
181
+
182
+ Semantic HTML, `:focus-visible` rings, ARIA where it matters. Not retrofitted — designed in.
183
+
184
+ </td>
185
+ <td valign="top">
186
+
187
+ ### 🪶 Tree-shakable
188
+
189
+ Named exports only. Import `Button`, leave the rest behind. No `Vue.use(KitchenSink)`.
190
+
191
+ </td>
192
+ <td valign="top">
193
+
194
+ ### 📖 Storybook included
195
+
196
+ Every component, every variant, every prop — browsable before you `npm install`.
197
+
198
+ </td>
199
+ </tr>
200
+ </table>
201
+
202
+ ---
203
+
204
+ ## 🧩 Components
205
+
206
+ <div align="center">
207
+
208
+ | Forms | Overlays | Layout | Display |
209
+ | :------------------- | :---------- | :------------ | :------ |
210
+ | Button | Dialog | Accordion | Badge |
211
+ | ButtonGroup | AlertDialog | AccordionItem | Tag |
212
+ | Input | Tooltip\* | Sidebar | Divider |
213
+ | Checkbox | | Tabs | Spinner |
214
+ | CheckboxGroup | | Breadcrumb | |
215
+ | Radio | | | |
216
+ | RadioGroup | | | |
217
+ | Select | | | |
218
+ | Switch / SwitchGroup | | | |
219
+ | Toggle / ToggleGroup | | | |
220
+ | Slider | | | |
221
+ | RichCheckboxGroup | | | |
222
+ | RichSwitchGroup | | | |
223
+
224
+ <sub>\* coming soon · _more on the way_</sub>
225
+
226
+ </div>
227
+
228
+ All components follow the same conventions: **validated props**, **declared emits**, **BEM-style hooks** (`ui-button--primary`), and `v-model` where it makes sense.
229
+
230
+ 👉 **Want to see them in action?** [Browse the live Storybook →](https://ui.webcake.io)
231
+
232
+ ---
233
+
234
+ ## 🎨 Theming
235
+
236
+ Every component reads from CSS custom properties. Drop them on `:root` and the whole kit rebrands:
237
+
238
+ ```css
239
+ :root {
240
+ --color-primary: #ff6b9d;
241
+ --color-primary-hover: #ff4d8a;
242
+ --radius-md: 12px;
243
+ --font-sans: 'Inter', system-ui, sans-serif;
244
+ }
245
+ ```
246
+
247
+ Dark mode? Already wired — every color token has a `.dark` companion. Toggle a class on `<html>` and you're done.
248
+
249
+ ```css
250
+ .dark {
251
+ --color-bg: #0b0b0f;
252
+ --color-fg: #f4f4f5;
253
+ /* …everything else cascades */
254
+ }
255
+ ```
256
+
257
+ No SASS variables. No JS theme provider. No runtime patching. Just CSS, the way the platform meant it.
258
+
259
+ ---
260
+
261
+ ## 🛡️ The dual-compat guarantee
262
+
263
+ Every component compiles cleanly on both runtimes — enforced by a strict authoring contract:
264
+
265
+ - ✅ **Pure Options API** — no `<script setup>`, no `setup()`, no Composition API imports
266
+ - ✅ **Single root template** (Vue 2 has no fragments)
267
+ - ✅ **`emits` always declared** (Vue 3 needs it for `$attrs` separation)
268
+ - ✅ No Vue-3-only features (`<Teleport>`, `<Suspense>`, multi `v-model`)
269
+ - ✅ No Vue-2-only features (filters, `.native`, `$listeners`)
270
+
271
+ And every commit runs through **four parallel build lanes**:
272
+
273
+ | | Runtime | Bundler | What it catches |
274
+ | :-: | :------ | :---------------------------------- | :------------------------------------ |
275
+ | 🟢 | Vue 3.4 | Vite + `@vitejs/plugin-vue` | Modern compile errors, fragment leaks |
276
+ | 🟣 | Vue 2.7 | webpack 4 + `vue-template-compiler` | Legacy compile errors, banned APIs |
277
+ | 🟡 | Vue 3.2 | Storybook 6.5 | Story-side regressions |
278
+ | 🧪 | Both | Vitest × 2 lanes | Runtime + behavior parity |
279
+
280
+ **If a PR doesn't build on either side, it doesn't ship.** Period.
281
+
282
+ ---
283
+
284
+ ## 🏗️ Why ship raw SFC?
285
+
286
+ Most Vue libraries publish pre-compiled `.js` artifacts targeting _one_ Vue runtime. That's how the ecosystem ended up fragmented into `-vue2` and `-vue3` packages.
287
+
288
+ webcake-ui-kit publishes the **source `.vue` files** directly. Your app's bundler — Vite, webpack, whatever you use — compiles them against _your_ Vue version. One source, two outputs, zero version lock-in.
289
+
290
+ > **The tradeoff:** you need a bundler that handles `.vue`. _(You already do.)_
291
+
292
+ This is the entire reason the kit exists. Everything else — the components, the tokens, the Storybook — is downstream of that decision.
293
+
294
+ ---
295
+
296
+ ## 🚦 Compatibility
297
+
298
+ | | Version | Status |
299
+ | :----------------- | :-------------------------------------- | :----------: |
300
+ | Vue 2 | `2.7.x` | ✅ supported |
301
+ | Vue 3 | `3.0+` (tested on `3.4`) | ✅ supported |
302
+ | Bundlers | Vite, webpack 4/5, Rollup, esbuild | ✅ |
303
+ | Node (consumer) | any version your bundler supports | ✅ |
304
+ | Node (contributor) | **14.x** (Vue 2 sandbox uses webpack 4) | ⚠️ pinned |
305
+
306
+ ---
307
+
308
+ ## 🧪 Local development
309
+
310
+ ```bash
311
+ npm run preview # Vue 2 + Vue 3 dev servers side by side (HMR on both)
312
+ npm run test:build # Compile-check everything (Vue 2, Vue 3, Storybook) — ~16s
313
+ npm test # Unit suite on both runtimes, in parallel
314
+ ```
315
+
316
+ Visit **`localhost:8001`** (Vue 3) and **`localhost:8080`** (Vue 2) side-by-side to spot dual-compat regressions in real time.
317
+
318
+ Full contributor workflow — including the 10-file checklist for adding a component — lives in [CLAUDE.md](./CLAUDE.md).
319
+
320
+ **Environment:** Node 14 · Python ≤ 3.10 (the Vue 2 playground uses webpack 4 with native deps).
321
+
322
+ ---
323
+
324
+ ## 🌐 Resources
325
+
326
+ <div align="center">
327
+
328
+ | | |
329
+ | :---------------------: | :------------------------------------------------------------------------------- |
330
+ | 🌍 **Docs & Storybook** | [ui.webcake.io](https://ui.webcake.io) |
331
+ | 📦 **npm** | [npmjs.com/package/webcake-ui-kit](https://www.npmjs.com/package/webcake-ui-kit) |
332
+ | 🐙 **GitHub** | [pancake-vn/webcake-ui-kit](https://github.com/pancake-vn/webcake-ui-kit) |
333
+ | 🐛 **Issues** | [Report a bug](https://github.com/pancake-vn/webcake-ui-kit/issues) |
334
+ | 💬 **Discussions** | [Ask a question](https://github.com/pancake-vn/webcake-ui-kit/discussions) |
335
+
336
+ </div>
337
+
338
+ ---
339
+
340
+ ## 🤝 Contributing
341
+
342
+ PRs welcome — especially new components, accessibility fixes, and Vue 2/3 parity bug reports.
343
+
344
+ Before opening one:
345
+
346
+ 1. Read [CLAUDE.md](./CLAUDE.md) — it documents the dual-compat rules and the 10-file checklist for adding a component.
347
+ 2. Run `npm run test:build && npm test` — both must pass on Vue 2 _and_ Vue 3 lanes.
348
+ 3. Add a Storybook story and a unit spec. New components without both are not accepted.
349
+
350
+ ---
351
+
352
+ ## 📜 License
353
+
354
+ **ISC** © Webcake Team — use it, fork it, ship it.
355
+
356
+ ---
357
+
358
+ <div align="center">
359
+ <sub>Made with 🍰 by <a href="https://github.com/pancake-vn">pancake-vn</a></sub>
360
+ <br />
361
+ <sub>Because nobody should have to rewrite their app to upgrade a UI library.</sub>
362
+ </div>
package/package.json CHANGED
@@ -1,14 +1,66 @@
1
1
  {
2
2
  "name": "webcake-ui-kit",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "UI Kit for Vue 2 && 3 - Pure Options API",
5
5
  "main": "src/index.js",
6
+ "module": "src/index.js",
7
+ "type": "module",
8
+ "sideEffects": [
9
+ "**/*.css"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "import": "./src/index.js",
14
+ "default": "./src/index.js"
15
+ },
16
+ "./styles": "./src/styles/index.css",
17
+ "./styles/*": "./src/styles/*",
18
+ "./WkAccordion": "./src/components/accordion/Accordion.vue",
19
+ "./WkAccordionItem": "./src/components/accordion-item/AccordionItem.vue",
20
+ "./WkAlertDialog": "./src/components/alert-dialog/AlertDialog.vue",
21
+ "./WkBadge": "./src/components/badge/Badge.vue",
22
+ "./WkBreadcrumb": "./src/components/breadcrumb/Breadcrumb.vue",
23
+ "./WkButton": "./src/components/button/Button.vue",
24
+ "./WkButtonGroup": "./src/components/button-group/ButtonGroup.vue",
25
+ "./WkCheckbox": "./src/components/checkbox/Checkbox.vue",
26
+ "./WkCheckboxGroup": "./src/components/checkbox-group/CheckboxGroup.vue",
27
+ "./WkDialog": "./src/components/dialog/Dialog.vue",
28
+ "./WkDivider": "./src/components/divider/Divider.vue",
29
+ "./WkInput": "./src/components/input/Input.vue",
30
+ "./WkPagination": "./src/components/pagination/Pagination.vue",
31
+ "./WkRadio": "./src/components/radio/Radio.vue",
32
+ "./WkRadioGroup": "./src/components/radio-group/RadioGroup.vue",
33
+ "./WkRichCheckboxGroup": "./src/components/rich-checkbox-group/RichCheckboxGroup.vue",
34
+ "./WkRichSwitchGroup": "./src/components/rich-switch-group/RichSwitchGroup.vue",
35
+ "./WkSelect": "./src/components/select/Select.vue",
36
+ "./WkSelectOption": "./src/components/select-option/SelectOption.vue",
37
+ "./WkSidebarGroupLabel": "./src/components/sidebar-group-label/SidebarGroupLabel.vue",
38
+ "./WkSidebarItem": "./src/components/sidebar-item/SidebarItem.vue",
39
+ "./WkSlider": "./src/components/slider/Slider.vue",
40
+ "./WkSpinner": "./src/components/spinner/Spinner.vue",
41
+ "./WkSwitch": "./src/components/switch/Switch.vue",
42
+ "./WkSwitchGroup": "./src/components/switch-group/SwitchGroup.vue",
43
+ "./WkTabs": "./src/components/tabs/Tabs.vue",
44
+ "./WkTag": "./src/components/tag/Tag.vue",
45
+ "./WkToggle": "./src/components/toggle/Toggle.vue",
46
+ "./WkToggleGroup": "./src/components/toggle-group/ToggleGroup.vue",
47
+ "./components/*": "./src/components/*",
48
+ "./package.json": "./package.json"
49
+ },
6
50
  "files": [
7
- "src"
51
+ "src",
52
+ "README.md",
53
+ "LICENSE"
8
54
  ],
55
+ "publishConfig": {
56
+ "access": "public"
57
+ },
9
58
  "scripts": {
10
59
  "dev": "vite",
11
60
  "build": "echo 'No need to build - shipping raw SFC'",
61
+ "sync:exports": "node scripts/sync-exports.js",
62
+ "sync:exports:check": "node scripts/sync-exports.js --check",
63
+ "prepublishOnly": "npm run sync:exports:check && npm run test:build",
12
64
  "dev:vue2": "cd playground-vue2 && npm run dev",
13
65
  "dev:vue3": "cd playground-vue3 && npm run dev",
14
66
  "preview": "concurrently --names vue3,vue2 --prefix-colors cyan,magenta \"npm:dev:vue3\" \"npm:dev:vue2\"",
@@ -17,10 +69,20 @@
17
69
  "build:storybook": "cd storybook-vue3 && npm run build-storybook",
18
70
  "test:build": "concurrently --kill-others-on-fail --names vue3,vue2,sb --prefix-colors cyan,magenta,yellow \"npm:build:vue3\" \"npm:build:vue2\" \"npm:build:storybook\"",
19
71
  "test:storybook": "npm run build:storybook",
72
+ "test:vue3": "cd playground-vue3 && npm test",
73
+ "test:vue2": "cd playground-vue2 && npm test",
74
+ "test": "concurrently --names vue3,vue2 --prefix-colors cyan,magenta \"npm:test:vue3\" \"npm:test:vue2\"",
20
75
  "format": "prettier --write .",
21
76
  "format:check": "prettier --check .",
22
77
  "lint": "eslint . --ext .js,.cjs,.vue",
23
- "lint:fix": "eslint . --ext .js,.cjs,.vue --fix"
78
+ "lint:fix": "eslint . --ext .js,.cjs,.vue --fix",
79
+ "prepare": "husky install || echo \"(husky skipped)\"",
80
+ "release": "node scripts/release.js",
81
+ "release:patch": "node scripts/release.js patch",
82
+ "release:minor": "node scripts/release.js minor",
83
+ "release:major": "node scripts/release.js major",
84
+ "release:fast": "node scripts/release.js patch --skip-checks",
85
+ "release:dry": "node scripts/release.js patch --dry-run"
24
86
  },
25
87
  "lint-staged": {
26
88
  "*.{js,cjs,vue}": [
@@ -40,6 +102,7 @@
40
102
  "eslint": "^8.57.1",
41
103
  "eslint-config-prettier": "^9.1.2",
42
104
  "eslint-plugin-vue": "^9.33.0",
105
+ "express": "^5.2.1",
43
106
  "husky": "^8.0.3",
44
107
  "lint-staged": "^13.3.0",
45
108
  "prettier": "^3.8.3",
@@ -54,9 +117,9 @@
54
117
  "UI Component"
55
118
  ],
56
119
  "author": "Webcake Team",
57
- "license": "ISC",
120
+ "license": "MIT",
58
121
  "bugs": {
59
122
  "url": "https://github.com/pancake-vn/webcake-ui-kit/issues"
60
123
  },
61
- "homepage": "https://github.com/pancake-vn/webcake-ui-kit#readme"
124
+ "homepage": "https://ui.webcake.io"
62
125
  }
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <div :class="['ui-accordion', bordered && 'ui-accordion--bordered']">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'Accordion',
10
+ model: { prop: 'value', event: 'change' },
11
+ props: {
12
+ bordered: { type: Boolean, default: false },
13
+ multiple: { type: Boolean, default: false },
14
+ value: { type: [Array, Number, String], default: undefined },
15
+ modelValue: { type: [Array, Number, String], default: undefined },
16
+ defaultOpen: { type: [Array, Number, String], default: () => [] }
17
+ },
18
+ emits: ['change', 'update:modelValue'],
19
+ provide() {
20
+ return {
21
+ accordion: this
22
+ }
23
+ },
24
+ data() {
25
+ const initial =
26
+ this.modelValue !== undefined ? this.modelValue : this.value !== undefined ? this.value : this.defaultOpen
27
+ return {
28
+ internalKeys: this.normalizeKeys(initial)
29
+ }
30
+ },
31
+ computed: {
32
+ isControlled() {
33
+ return this.modelValue !== undefined || this.value !== undefined
34
+ },
35
+ openKeys() {
36
+ if (this.modelValue !== undefined) return this.normalizeKeys(this.modelValue)
37
+ if (this.value !== undefined) return this.normalizeKeys(this.value)
38
+ return this.internalKeys
39
+ }
40
+ },
41
+ methods: {
42
+ normalizeKeys(v) {
43
+ if (Array.isArray(v)) return v.slice()
44
+ if (v === undefined || v === null || v === '') return []
45
+ return [v]
46
+ },
47
+ isOpen(key) {
48
+ return this.openKeys.indexOf(key) !== -1
49
+ },
50
+
51
+ toggle(key) {
52
+ const isOpen = this.isOpen(key)
53
+ let newKeys
54
+ if (this.multiple) {
55
+ newKeys = isOpen ? this.openKeys.filter(k => k !== key) : this.openKeys.concat([key])
56
+ } else {
57
+ newKeys = isOpen ? [] : [key]
58
+ }
59
+ const emitVal = this.multiple ? newKeys : newKeys.length > 0 ? newKeys[0] : null
60
+ if (!this.isControlled) {
61
+ this.internalKeys = newKeys
62
+ }
63
+ this.$emit('change', emitVal)
64
+ this.$emit('update:modelValue', emitVal)
65
+ }
66
+ }
67
+ }
68
+ </script>
69
+
70
+ <style src="./accordion.css" scoped></style>
@@ -0,0 +1,5 @@
1
+ .ui-accordion {
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 100%;
5
+ }
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'ui-accordion-item',
5
+ bordered && 'ui-accordion-item--bordered',
6
+ isOpen && 'ui-accordion-item--open',
7
+ disabled && 'ui-accordion-item--disabled'
8
+ ]"
9
+ >
10
+ <button
11
+ :id="triggerId"
12
+ type="button"
13
+ class="ui-accordion-item__trigger"
14
+ :disabled="disabled"
15
+ :aria-expanded="isOpen ? 'true' : 'false'"
16
+ :aria-controls="contentId"
17
+ @click="onClick"
18
+ >
19
+ <span class="ui-accordion-item__label-row">
20
+ <span class="ui-accordion-item__label">
21
+ <slot name="label">{{ label }}</slot>
22
+ </span>
23
+ <span v-if="hasAppend" class="ui-accordion-item__append">
24
+ <slot name="append">{{ append }}</slot>
25
+ </span>
26
+ </span>
27
+ <span :class="['ui-accordion-item__icon', isOpen && 'ui-accordion-item__icon--open']">
28
+ <slot name="icon">
29
+ <svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
30
+ <path
31
+ d="m4 6 4 4 4-4"
32
+ stroke="currentColor"
33
+ stroke-width="1.5"
34
+ stroke-linecap="round"
35
+ stroke-linejoin="round"
36
+ />
37
+ </svg>
38
+ </slot>
39
+ </span>
40
+ </button>
41
+ <div v-show="isOpen" :id="contentId" class="ui-accordion-item__content" role="region" :aria-labelledby="triggerId">
42
+ <slot />
43
+ </div>
44
+ </div>
45
+ </template>
46
+
47
+ <script>
48
+ let uidCounter = 0
49
+
50
+ export default {
51
+ name: 'AccordionItem',
52
+ inject: {
53
+ accordion: { default: null }
54
+ },
55
+ props: {
56
+ label: { type: String, default: '' },
57
+ value: { type: [String, Number], default: undefined },
58
+ append: { type: String, default: '' },
59
+ disabled: { type: Boolean, default: false }
60
+ },
61
+ data() {
62
+ return {
63
+ uid: `accordion-item-${++uidCounter}`
64
+ }
65
+ },
66
+ computed: {
67
+ itemKey() {
68
+ return this.value !== undefined ? this.value : this.uid
69
+ },
70
+ triggerId() {
71
+ return `${this.uid}-trigger`
72
+ },
73
+ contentId() {
74
+ return `${this.uid}-content`
75
+ },
76
+ isOpen() {
77
+ return this.accordion ? this.accordion.isOpen(this.itemKey) : false
78
+ },
79
+ bordered() {
80
+ return this.accordion ? this.accordion.bordered : false
81
+ },
82
+ hasAppend() {
83
+ const slots = this.$slots
84
+ return !!this.append || !!(slots && slots['append'])
85
+ }
86
+ },
87
+ methods: {
88
+ onClick() {
89
+ if (this.disabled) return
90
+ if (this.accordion && typeof this.accordion.toggle === 'function') {
91
+ this.accordion.toggle(this.itemKey)
92
+ }
93
+ }
94
+ }
95
+ }
96
+ </script>
97
+
98
+ <style src="./accordion-item.css" scoped></style>