Compare commits

..

355 Commits

Author SHA1 Message Date
Benjamin Canac
1e05b0f072 chore(release): 2.4.1 2023-06-21 17:53:38 +02:00
Benjamin Canac
87e98f038a chore(deps): migrate from standard-version to release-it 2023-06-21 17:53:21 +02:00
Benjamin Canac
f7e2082983 fix(module): safelist regex when a : was present before color
Also prevents parsing colors already safelisted initially.
2023-06-21 17:42:02 +02:00
Benjamin Canac
f719111abb fix(module): safelist aliases for input
To make it work when doing `<USelect color="yellow" />` for example
2023-06-21 17:41:51 +02:00
Benjamin Canac
3bac0874f1 fix(Radio/Checkbox): remove legacy custom 2023-06-21 17:41:39 +02:00
Selemondev
457b7a9fb7 fix(forms): precise type assertion for onInput event handler (#293)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-06-21 17:41:15 +02:00
Benjamin Canac
4023fbec29 fix(module): let tailwindcss viewer enabled by default
Resolves #292
2023-06-21 17:40:54 +02:00
Benjamin Canac
a274a0cdbb chore(release): 2.4.0 2023-06-13 17:42:38 +02:00
Benjamin Canac
717a514451 fix(SelectMenu): input focus after be5f352 2023-06-13 17:34:34 +02:00
9uenther
786d7765f5 fix(Table): colspan of empty and loading is wrong when selection enabled (#284) 2023-06-13 17:06:06 +02:00
Benjamin Canac
a733c13866 fix(module): hardcode gray safelist instead of deduplicate complex logic 2023-06-13 16:31:13 +02:00
Benjamin Canac
88c1930845 fix(module): transform vue files to detect multi-line components 2023-06-13 15:53:02 +02:00
Benjamin Canac
c3f5c44461 docs: improve theming colors safelisting section 2023-06-13 15:51:05 +02:00
Benjamin Canac
2cfa1f8d03 fix(module): deduplicate default safelist as components may share same rules 2023-06-13 15:27:25 +02:00
Benjamin Canac
5f7de8e595 docs: only safelist valid colors 2023-06-13 15:26:45 +02:00
Benjamin Canac
cdce519742 fix(module): only safelist known colors 2023-06-13 14:52:56 +02:00
Benjamin Canac
ccd9ca5106 fix(module): prevent safelisting dynamic :color variables 2023-06-13 14:52:32 +02:00
Benjamin Canac
9031742acc chore(deps): bump 2023-06-13 14:52:04 +02:00
Benjamin Canac
9559d0b3bc fix(deps): move @tailwindcss/container-queries to dependencies 2023-06-13 12:27:17 +02:00
Benjamin Canac
0e6550ec45 chore(deps): bump 2023-06-13 12:25:37 +02:00
Benjamin Canac
20fa4d2317 feat(module): smart safelisting (#268)
Co-authored-by: Sébastien Chopin <seb@nuxtjs.com>
2023-06-13 12:18:38 +02:00
Benjamin Canac
e12e9740c9 fix(forms)!: bind $attrs to elements (#279) 2023-06-13 11:35:05 +02:00
Benjamin Canac
cbc8ef13cc fix(CommandPalette): input focus after be5f352 2023-06-12 14:56:13 +02:00
Haytham A. Salama
652af93f5c feat(CommandPalette): handle empty-state (#271)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-06-12 14:54:34 +02:00
Benjamin Canac
b4a96a8b01 chore(Select)!: rename text-attribute to option-attribute and defaults to label 2023-06-12 14:42:33 +02:00
Benjamin Canac
bc81d45b2b docs: improve forms usage with examples 2023-06-12 14:42:33 +02:00
Benjamin Canac
429791dab0 fix(Radio/Checkbox): split preset as indeterminate is checkbox only 2023-06-12 14:42:33 +02:00
Benjamin Canac
fe833eb2b2 fix(Toggle): missing disabled prop 2023-06-12 14:42:17 +02:00
Benjamin Canac
be5f352296 fix(module): use @tailwindcss/forms class strategy (#278) 2023-06-12 14:27:08 +02:00
Haytham A. Salama
47415322ea feat(table): add loading state (#259)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-06-12 14:26:29 +02:00
Benjamin Canac
d20983d355 docs: add Edge badge for next release features
Resolves #277
2023-06-12 10:53:06 +02:00
Sylvain Marroufin
f0b24ba25d feat(Pagination): new component (#257)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
Co-authored-by: Haytham A. Salama <haythamasalama@gmail.com>
2023-06-09 18:12:40 +02:00
Haytham A. Salama
f7a34c8fee feat(table): add slot for empty state (#260)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-06-09 17:06:42 +02:00
Benjamin Canac
4e5e614eb4 docs: notification color default to primary 2023-06-09 14:32:53 +02:00
Benjamin Canac
07f7855a26 fix(Notification): class priority for icon color 2023-06-09 11:40:46 +02:00
Benjamin Canac
57f95102e2 chore: add @tailwindcss/container-queries official plugin 2023-06-09 11:39:46 +02:00
Benjamin Canac
3f8d927438 chore(Dropdown): handle height for overflow 2023-06-08 12:55:38 +02:00
Benjamin Canac
d91c0bb894 fix(ButtonGroup): use -space-x-px on wrapper 2023-06-06 15:58:24 +02:00
Benjamin Canac
a6176720c7 fix(ButtonGroup): invalid size validator 2023-06-06 14:59:18 +02:00
Benjamin Canac
a6903df58f fix(Button): same size when no label + uniformize form elements 2023-06-06 12:30:03 +02:00
Benjamin Canac
19b149518e docs: add space between header icons 2023-06-06 12:26:34 +02:00
Benjamin Canac
c66a99a60f docs: only display links section in header when needed 2023-06-06 11:48:16 +02:00
Benjamin Canac
4a7c6035b6 chore(deps): bump 2023-06-06 10:33:17 +02:00
Benjamin Canac
207444fdea fix(forms): padded prop with p-0 class 2023-06-06 10:33:11 +02:00
Benjamin Canac
60eea0e46b chore(release): 2.3.0 2023-06-05 12:12:48 +02:00
Benjamin Canac
5e50eb9eb8 fix: use cloneVNode when altering props in render functions
Resolves #252

https://vuejs.org/api/render-function.html#clonevnode
2023-06-05 11:17:31 +02:00
Dominik Opyd
af65683123 docs(github): support for various file extensions (#250) 2023-06-02 11:35:31 +02:00
Benjamin Canac
2c673f5377 fix(CommandPalette): override of closeButton and emptyState props 2023-06-01 17:15:04 +02:00
Benjamin Canac
192b0e6301 fix(Table): override of sortButton and emptyState props 2023-06-01 17:14:45 +02:00
Benjamin Canac
71edb91c4f fix(Avatar): placeholder font size 2023-06-01 16:47:23 +02:00
Benjamin Canac
f9b935f5f5 fix(Badge): remove console.log in validator 2023-06-01 16:24:19 +02:00
Benjamin Canac
23833e92cb chore(Badge): handle color override like buttons 2023-06-01 16:06:07 +02:00
Benjamin Canac
241df7f05e docs: fix toc scroll when duplicated names 2023-06-01 15:41:23 +02:00
Benjamin Canac
130a1f2c54 docs: improve slots sections 2023-06-01 15:29:02 +02:00
Benjamin Canac
c63981e31c docs: :component-card now handle slots 2023-06-01 15:28:39 +02:00
Benjamin Canac
687f0c6f63 docs: improve inline code blocks inside Alert component 2023-06-01 15:28:12 +02:00
Benjamin Canac
f59a92ca15 chore(Input)!: move pointer class inside its own preset class 2023-06-01 15:27:53 +02:00
Benjamin Canac
01fa85c7a3 fix(defineShortcuts): err with input autocomplete that triggers keydown 2023-06-01 15:26:46 +02:00
Benjamin Canac
3434bc7f2b chore(deps): bump @nuxthq/studio 2023-06-01 11:43:47 +02:00
Benjamin Canac
9b1aacb1da docs: move slots sections as h2 2023-06-01 11:39:07 +02:00
Benjamin Canac
8951923a11 fix(SelectMenu): disable on loading 2023-06-01 11:08:16 +02:00
Benjamin Canac
e200d4cc74 chore(package): remove preinstall script 2023-06-01 10:49:36 +02:00
Benjamin Canac
e05619f8c8 chore: add leading and trailing slots
Resolves #246
2023-05-31 23:53:31 +02:00
Benjamin Canac
5ea43ab4e4 chore: uniformize icons in Button / Input / Select / SelectMenu
Also adds `loading` to `Select` and `SelectMenu`
2023-05-31 23:30:52 +02:00
Benjamin Canac
ba44c58a80 chore(SelectMenu)!: remove inline-flex from wrapper to behave like other form elements 2023-05-31 23:22:58 +02:00
Benjamin Canac
490025a981 docs: add Table icons in theming icons section 2023-05-31 18:31:15 +02:00
Benjamin Canac
2966373a86 chore(Table): handle empty-state
Resolves #243
2023-05-31 18:30:49 +02:00
Sylvain Marroufin
8bdb8c45f7 chore(Dropdown): hover mode with padding instead of offset + improve docs (#242)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-05-31 14:28:14 +02:00
Sylvain Marroufin
9827de0b58 docs(textarea): improve props documentation (#241) 2023-05-31 12:49:47 +02:00
Benjamin Canac
23f01fde41 chore(Table): rename preset container to base 2023-05-31 12:11:28 +02:00
Benjamin Canac
f680318e44 docs: fix overflow in table page
Fixes #244
2023-05-31 12:11:12 +02:00
Benjamin Canac
cd2d1eb1fa docs(table): add alert on sort prop without sortable 2023-05-30 18:06:03 +02:00
Benjamin Canac
3ba0aedcba fix(Table): type sort prop 2023-05-30 18:03:24 +02:00
Benjamin Canac
40b6884424 chore(Table): handle default sort and default column direction 2023-05-30 17:59:30 +02:00
Benjamin Canac
a2638c6057 chore(Table): split container divide 2023-05-30 16:23:17 +02:00
Benjamin Canac
6bd5142a37 fix(Table): add missing text-left in th.base 2023-05-30 16:21:54 +02:00
Benjamin Canac
bc1d653857 chore(Table): split preset for th and td 2023-05-30 16:17:17 +02:00
Benjamin Canac
6c215e07a6 chore(deps): bump 2023-05-30 12:18:05 +02:00
Benjamin Canac
272af9d24c fix(Table): missing ref import from vue 2023-05-30 12:17:59 +02:00
Benjamin Canac
cce000ab2b feat: add Table component (#237) 2023-05-30 12:13:57 +02:00
Benjamin Canac
4a99d6a7bb docs: fix notification preset for closeButton and actionButton 2023-05-29 22:45:33 +02:00
Benjamin Canac
4458656be5 chore(Notification)!: rename to closeButton and actionButton for consistency 2023-05-29 21:59:59 +02:00
Benjamin Canac
daca46371c chore(CommandPalette)!: rename props to emptyState and closeButton for consistency 2023-05-29 21:55:08 +02:00
Benjamin Canac
8ee2ac10e7 chore(Toggle)!: rename icons to onIcon / offIcon for consistency 2023-05-29 21:38:51 +02:00
Benjamin Canac
1ebaa5aa00 fix(Button): invalid padding when using square prop 2023-05-29 21:36:28 +02:00
Benjamin Canac
cb43548305 chore(SelectMenu): handle multiple default display + specific placeholder 2023-05-29 11:52:02 +02:00
Benjamin Canac
360084af7c chore(Toggle): improve component
- allow `iconOn` / `iconOff` default values from preset
- `bg-gray-900` on dark mode inside of `bg-white`
- added `name` prop for form control
2023-05-27 22:27:31 +02:00
Benjamin Canac
0af5184c70 chore(release): 2.2.1 2023-05-27 12:27:53 +02:00
Benjamin Canac
44c3e2c46a chore(forms): remove required on Input, Select and Textarea name
Resolves #236
2023-05-27 12:03:29 +02:00
Benjamin Canac
a96dc19215 fix(FormGroup): missing h import from vue
Resolves #236
2023-05-27 12:02:51 +02:00
Benjamin Canac
aa881a8d00 chore(release): 2.2.0 2023-05-26 23:19:53 +02:00
Benjamin Canac
08413f198b scripts: update to pnpm 2023-05-26 22:46:17 +02:00
Benjamin Canac
75ab1d2ed5 chore(deps): bump 2023-05-26 22:25:58 +02:00
Sumit Kolhe
2d6ce654f4 docs: add close button to Slideover example (#211)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-05-26 22:20:34 +02:00
Benjamin Canac
9ce531a06f feat!: handle color states on form elements (#234) 2023-05-26 22:07:49 +02:00
Benjamin Canac
1a9dc5c980 fix(Notification): remove default color on icon 2023-05-26 18:28:52 +02:00
Benjamin Canac
589f86ef1b chore(Avatar): dark variant for chip background color 2023-05-26 18:28:32 +02:00
Benjamin Canac
1b61ec72e2 chore(Notification)!: rename progressColor to color and style icon
This also removes `progressVariant` prop
2023-05-26 18:03:54 +02:00
Benjamin Canac
1f22f84360 chore(Avatar)!: remove chipVariant prop 2023-05-26 18:02:48 +02:00
Benjamin Canac
2c6db975f9 chore(deps): switch to pnpm (#228) 2023-05-26 17:41:07 +02:00
Benjamin Canac
b7099aa0d3 chore(SelectMenu): add searchablePlaceholder prop
Resolves #231
2023-05-26 15:02:21 +02:00
Benjamin Canac
36b0869bc2 docs: fix prev card gap on first page 2023-05-23 16:57:42 +02:00
Benjamin Canac
28167e41ff docs: add VerticalNavigation tailwind example 2023-05-23 15:27:13 +02:00
Benjamin Canac
19923cbf1e chore(VerticalNavigation)!: split preset 2023-05-23 15:26:47 +02:00
Benjamin Canac
1210e99ec1 chore(VerticalNavigation): improve types import 2023-05-23 15:25:28 +02:00
Benjamin Canac
fc894bc1ae chore(Dropdown): improve types import 2023-05-23 15:25:12 +02:00
Benjamin Canac
9491ac7172 chore(CommandPalette): improve types import 2023-05-23 15:25:00 +02:00
Benjamin Canac
32dc2264d8 chore(types): export button 2023-05-23 15:24:41 +02:00
Benjamin Canac
45ba3b26da chore(Notification): improve types 2023-05-23 15:24:32 +02:00
Benjamin Canac
6d3309c42d chore(Notification): move padding to app.config 2023-05-23 11:25:56 +02:00
Benjamin Canac
530b85136d docs: handle color mode in volta embed 2023-05-23 11:11:19 +02:00
Benjamin Canac
cb9ed9ad3f chore(Button): inject NuxtLink in components 2023-05-22 19:05:39 +02:00
Benjamin Canac
524e220914 chore(VerticalNavigation): improve binds & types 2023-05-22 19:05:17 +02:00
Benjamin Canac
e3e6ef27a2 docs: improve Dropdown example with click and disabled 2023-05-22 19:04:39 +02:00
Benjamin Canac
55f115f9fe chore(Dropdown): use ULinkCustom + improve item binds & types
Fixes #215
2023-05-22 19:04:18 +02:00
Benjamin Canac
bdaf2dbbd4 chore(CommandPalette): handle loading state (#221) 2023-05-22 16:00:31 +02:00
Benjamin Canac
e7eea067b2 chore(Notification): add progressColor and progressVariant props (#219)
Co-authored-by: Sébastien Chopin <seb@nuxtjs.com>
2023-05-22 15:01:19 +02:00
Benjamin Canac
a56dbeab35 fix(Radio/Checkbox): remove ring offset on focus 2023-05-22 13:41:56 +02:00
Benjamin Canac
570b82d1e7 chore(Avatar): allow default value for chipColor through app.config.ts 2023-05-22 12:24:17 +02:00
Harry Yep
b5189c0c07 docs: LogoLabs not shown (#216) 2023-05-21 23:01:08 +02:00
Sébastien Chopin
8a0a5d8ba0 docs: pre-render component-meta routes 2023-05-20 19:13:58 +02:00
Sébastien Chopin
d3e5f4e15d docs: remove console.log 2023-05-20 18:53:32 +02:00
Sébastien Chopin
5a592b7ee0 docs: use CF rules for redirect 2023-05-20 18:49:39 +02:00
Sébastien Chopin
43787eca74 docs: move vercel.json to public dir 2023-05-20 18:41:07 +02:00
Sébastien Chopin
595ed9fb46 docs: add vercel redirect 2023-05-20 18:38:19 +02:00
Sébastien Chopin
5c4ab26d25 docs: support ssg 2023-05-20 18:31:56 +02:00
Sébastien Chopin
2030f24a47 docs: update logo on aside on mobile 2023-05-20 13:18:19 +02:00
Benjamin Canac
6eda322496 chore(VerticalNavigation): links badge type as number
Resolves #206
2023-05-19 15:55:18 +02:00
Benjamin Canac
318f8b2f08 docs: improve theming colors section 2023-05-19 15:00:39 +02:00
Benjamin Canac
dfab900562 docs: add badge in VerticalNavigation example 2023-05-19 14:51:31 +02:00
Benjamin Canac
d2ee5058f8 fix(VerticalNavigation): badge display
Resolves #205
2023-05-19 14:51:16 +02:00
Benjamin Canac
e358183165 docs: getting started title on index 2023-05-19 13:01:34 +02:00
Benjamin Canac
26579538f5 docs: prevent Alert text hover without link 2023-05-19 13:00:50 +02:00
Benjamin Canac
f99b9e283a chore(release): 2.1.0 2023-05-19 12:25:37 +02:00
Benjamin Canac
246449b328 fix(module): add .mjs extension to tailwind content when builded
Resolves #172
2023-05-19 12:22:05 +02:00
Benjamin Canac
ea740bf10a fix(Textarea): expose ref 2023-05-19 12:15:40 +02:00
Benjamin Canac
2ded24bec9 fix(Input): expose ref 2023-05-19 12:15:33 +02:00
Benjamin Canac
180a1df374 chore(CommandPalette): remove useless ref 2023-05-19 12:13:32 +02:00
Benjamin Canac
b9edf31aed chore(Button): remove useless ref 2023-05-19 12:12:59 +02:00
Benjamin Canac
526f84692e docs: exclude label prop on button icon section 2023-05-19 11:56:29 +02:00
Benjamin Canac
d66f4c5e46 docs: reduce gap on prev next card 2023-05-19 11:56:17 +02:00
Benjamin Canac
ec3fd88472 docs: rename xxs to 2xs in button group section
Resolves #203
2023-05-19 11:50:21 +02:00
Benjamin Canac
0b097352b4 docs: theming icons with all param 2023-05-19 11:28:52 +02:00
Benjamin Canac
85b10ba4ee chore(module): handle icons as string 2023-05-19 11:28:24 +02:00
Benjamin Canac
4ac0e0c481 docs: update edit on github link icon 2023-05-19 11:22:02 +02:00
Benjamin Canac
617567d6e5 chore(SelectMenu): empty option shorter text 2023-05-19 11:20:30 +02:00
Sébastien Chopin
e59fe42cc9 docs: improve page descriptions and prev/next (#184)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-05-19 10:33:09 +02:00
Sébastien Chopin
1743ea91d5 chore(Notifications): simplify how to position them (#190)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-05-19 10:01:43 +02:00
BoBruce
fa59ea9d83 docs: fix copy button overlap code content (#194) 2023-05-18 17:57:35 +02:00
Benjamin Canac
efb9e8a263 docs: add _blank target on stackblitz link 2023-05-17 17:46:59 +02:00
Sébastien Chopin
0931372157 docs(footer): fix when page is not defined 2023-05-17 17:17:22 +02:00
Arash
7f00ec6c3d fix(DocsPageHeader): github component link (#182) 2023-05-17 17:00:36 +02:00
Benjamin Canac
2bdeb04f56 docs: add CommandPalette async search example 2023-05-17 15:13:34 +02:00
Benjamin Canac
c834f401cd docs: lint on page footer button 2023-05-17 15:03:48 +02:00
Benjamin Canac
421f3bd2be chore(github): add stackblitz link in bug report issue template 2023-05-17 15:00:50 +02:00
Benjamin Canac
e63d5b74fc docs: add stackblitz playground link in installation page 2023-05-17 14:57:07 +02:00
Benjamin Canac
9ffcef9cd0 docs: add footer with nuxtlabs and github edit links 2023-05-17 14:56:54 +02:00
Benjamin Canac
1aa8376d4f docs: add Select component description 2023-05-17 13:12:11 +02:00
Benjamin Canac
94f24da723 docs: volta embed url with gray and primary 2023-05-17 13:08:01 +02:00
Sébastien Chopin
44457a0530 chore: fix node engine version (#176)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-05-17 12:46:41 +02:00
ciaasteczkowy
3fa10aa4eb fix(ButtonGroup/AvatarGroup): allow v-for (#173)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-05-17 12:46:26 +02:00
Benjamin Canac
ead46f6798 chore(deps): use @nuxt/eslint-config 2023-05-17 12:30:03 +02:00
Benjamin Canac
1bfcf31964 docs: add roadmap page with volta board 2023-05-17 12:16:52 +02:00
Benjamin Canac
b795f0f65b docs: handle layout without toc 2023-05-17 12:13:24 +02:00
Benjamin Canac
d93e995298 chore(colors): prevent err and add warning when specific colors dont exist 2023-05-17 11:02:29 +02:00
Benjamin Canac
00641b1439 docs: add warning to prevent double module installation 2023-05-17 10:59:48 +02:00
Benjamin Canac
07197c531e chore(github): add issue templates 2023-05-17 10:05:23 +02:00
Žan Fras
4ac9a613e3 docs: update icon theming installation commands (#166) 2023-05-17 09:54:17 +02:00
Manaus
5336436da0 docs: use preferred naming for Tailwind CSS (#168) 2023-05-17 09:52:44 +02:00
Benjamin Canac
7588eca148 docs: use a single svg icon 2023-05-16 17:33:31 +02:00
Sébastien Chopin
c0a4b28f54 docs: add badges 2023-05-16 16:52:35 +02:00
Benjamin Canac
157e34f2c0 docs: put DocsSearch on client only 2023-05-16 16:48:49 +02:00
Benjamin Canac
6675bcf396 docs: add favicon 2023-05-16 16:48:48 +02:00
Sébastien Chopin
3d43fc7a32 docs: Update README.md 2023-05-16 16:47:00 +02:00
Benjamin Canac
b3b349aab7 docs: improve icons installations with all package managers 2023-05-16 16:15:55 +02:00
Benjamin Canac
ab67659412 docs: fix command palette examples links 2023-05-16 15:23:29 +02:00
Benjamin Canac
c090a9e9cd docs: improve seo 2023-05-16 15:20:58 +02:00
Benjamin Canac
5e3357b696 docs: add social-preview 2023-05-16 15:20:48 +02:00
Benjamin Canac
11941bd581 chore(module): default primary to green 2023-05-16 15:18:12 +02:00
Benjamin Canac
036658b6de chore(readme): update 2023-05-16 15:17:54 +02:00
Benjamin Canac
86dd6092f3 docs: add @nuxthq/studio and @nuxt/devtools 2023-05-16 15:17:04 +02:00
Benjamin Canac
cba95de72b chore(Container): add as prop 2023-05-16 12:57:35 +02:00
Benjamin Canac
4d8dbfb912 chore(Card): add as prop 2023-05-16 12:57:35 +02:00
Benjamin Canac
703fdef9bd fix(app.config): trailing space 2023-05-16 12:57:35 +02:00
Sylvain Marroufin
c78d5ab0d3 docs(shortcuts): add config details (#155) 2023-05-16 12:48:10 +02:00
Benjamin Canac
10f3c0c271 docs: improve index description 2023-05-16 12:07:05 +02:00
Benjamin Canac
370c31296a chore(license): rename to md 2023-05-16 12:03:05 +02:00
Benjamin Canac
f5b57bb3c8 chore(license): add 2023-05-16 12:02:04 +02:00
Benjamin Canac
8286d15d1e chore(readme): improve 2023-05-16 12:01:58 +02:00
Benjamin Canac
f528a5ebc0 docs: move dark mode in theming 2023-05-16 12:00:33 +02:00
Benjamin Canac
5fea0eb8d5 chore(deps): bump @nuxtjs/tailwindcss 2023-05-15 19:02:38 +02:00
Benjamin Canac
5ede417bd8 chore(deps): bump 2023-05-15 14:14:19 +02:00
Benjamin Canac
066b239299 chore(release): 2.0.4 2023-05-15 14:08:04 +02:00
Benjamin Canac
97da6c6343 chore(SelectMenu): add required input only when needed 2023-05-15 14:07:41 +02:00
Benjamin Canac
6d9e4d424b docs: improve alert code blocks on light mode 2023-05-15 14:07:23 +02:00
Benjamin Canac
52192a4ac0 docs: add input group section 2023-05-15 14:07:14 +02:00
Benjamin Canac
a6c53e4d20 chore(app.config): improve forms required 2023-05-15 14:07:02 +02:00
Benjamin Canac
e8b46540d8 fix(SelectMenu): add missing inline-flex on wrapper 2023-05-15 12:15:11 +02:00
Benjamin Canac
a01e0279a9 chore(release): 2.0.3 2023-05-15 11:22:09 +02:00
Benjamin Canac
ccbf3e78b1 docs: typo in button 2023-05-15 11:21:51 +02:00
Benjamin Canac
dcc0a6b3a9 docs: minor improvements 2023-05-14 17:07:11 +02:00
Benjamin Canac
bb4cd0b1b9 docs: add CommandPalette full-text search section 2023-05-14 17:06:31 +02:00
Benjamin Canac
ae02f23a8c chore(Kbd): usage with value prop 2023-05-14 17:06:08 +02:00
Benjamin Canac
8caa78819a chore(Skeleton): new component 2023-05-14 17:05:38 +02:00
Benjamin Canac
6fd5a70ac9 docs: improve ContextMenu example 2023-05-14 15:08:16 +02:00
Benjamin Canac
cfcd2f1371 docs: uniformize isOpen in examples 2023-05-14 15:05:33 +02:00
Benjamin Canac
c47f5e6a68 docs: improve forms cards 2023-05-14 15:04:56 +02:00
Benjamin Canac
37f1a1b5ad docs: lots of improvements 2023-05-13 23:22:07 +02:00
Benjamin Canac
0c2a5d98cf chore(SelectMenu): use trailingIcon default from Select for consistency 2023-05-13 23:21:32 +02:00
Benjamin Canac
aabf1dd9eb chore(release): 2.0.2 2023-05-11 17:12:50 +02:00
Benjamin Canac
c7c78cb47b fix(LinkCustom): handle button when no to prop 2023-05-11 17:03:02 +02:00
Benjamin Canac
3335a6a32c docs: put back router.options.ts 2023-05-11 16:51:30 +02:00
Benjamin Canac
15f9db9420 chore(release): 2.0.1 2023-05-11 16:26:11 +02:00
Benjamin Canac
aacb7e9841 fix(CommandPalette): put back searchable on v-show to input ref always exists 2023-05-11 15:45:33 +02:00
Benjamin Canac
3ded73194d docs: improve command palettes 2023-05-11 15:24:26 +02:00
Benjamin Canac
82adedf764 docs: improve search 2023-05-11 15:18:01 +02:00
Benjamin Canac
192bf4c375 fix(CommandPalette): expose input ref to template 2023-05-11 15:17:47 +02:00
Benjamin Canac
59fc14e93f chore(SelectMenu): add prop for trailingIcon and fix padding when selected 2023-05-11 14:28:58 +02:00
Benjamin Canac
0d83366427 chore(Select): add prop for trailingIcon 2023-05-11 14:21:30 +02:00
Benjamin Canac
cc65afafbd chore(CommandPaletteGroup): rename ui.group.command.selected.icon to ui.group.command.selectedIcon.base 2023-05-11 14:21:01 +02:00
Benjamin Canac
c7e0cb40e7 chore(CommandPaletteGroup): style <mark> with primary 2023-05-11 14:18:14 +02:00
Benjamin Canac
24434dc561 docs: default value in select menu basic example 2023-05-11 14:17:28 +02:00
Benjamin Canac
6c35ee9270 docs: typo in context-menu 2023-05-11 14:17:12 +02:00
Benjamin Canac
950b341696 docs: prevent aside to scroll up on page change 2023-05-11 14:17:01 +02:00
Benjamin Canac
24e7109959 chore(deps): bump 2023-05-10 18:35:24 +02:00
Benjamin Canac
be96824323 chore(SelectMenu): improve option.selected preset 2023-05-10 18:33:43 +02:00
Benjamin Canac
d5471f4d37 fix(Toggle): wrong icon-off positioning 2023-05-10 18:14:21 +02:00
Benjamin Canac
00b444b3eb chore: rename spacing to padding 2023-05-10 16:24:28 +02:00
Benjamin Canac
76a0d61a0f fix(colors): missing useNuxtApp import 2023-05-10 12:39:08 +02:00
Benjamin Canac
6d79548ee8 chore(Slideover): default side to right 2023-05-10 12:27:20 +02:00
Benjamin Canac
f48ead6faf fix(docs): sticky search button z-index 2023-05-10 11:59:30 +02:00
Benjamin Canac
fd4c80acd4 fix(Avatar): gray missing for chipColor 2023-05-10 11:59:16 +02:00
Benjamin Canac
3df917ae70 chore(deps): fix vue-tsc version to 1.6.3 2023-05-10 00:22:10 +02:00
Benjamin Canac
19a34c44da chore(plugins): move hexToRgb fn to utils 2023-05-10 00:19:27 +02:00
Benjamin Canac
365c843fc0 chore(SelectMenu): put back required input 2023-05-10 00:18:14 +02:00
Benjamin Canac
cd430a4cad fix(Icon): missing import 2023-05-10 00:17:20 +02:00
Benjamin Canac
32ada0b28b chore(deps): bump @nuxtjs/tailwindcss 2023-05-09 18:55:28 +02:00
Benjamin Canac
034a95d3c9 fix(VerticalNavigation): improve focus 2023-05-09 18:55:15 +02:00
Benjamin Canac
530d8a8c27 chore(Dropdown): improve preset 2023-05-09 17:58:07 +02:00
Benjamin Canac
939efba47c fix(app.config): remove old u- classes 2023-05-09 16:26:12 +02:00
Benjamin Canac
c43c212ae1 chore(Toggle): shrink size and move focus to focus-visible 2023-05-09 16:25:09 +02:00
Benjamin Canac
0404c871fb chore(Radio/Checkbox): change focus to focus-visible 2023-05-09 16:24:36 +02:00
Benjamin Canac
ebf5fd6aeb fix(Avatar): shrink chip ring 2023-05-09 16:23:51 +02:00
Benjamin Canac
d5d250b8cf chore(Badge): default size to sm and add lg 2023-05-09 14:30:43 +02:00
Benjamin Canac
410d2351d6 chore(Avatar): default size to sm 2023-05-09 14:30:43 +02:00
Benjamin Canac
949a476125 docs: add fallback on colorMode button 2023-05-09 14:30:43 +02:00
Benjamin Canac
4586eed90c chore(Kbd): new component 2023-05-09 14:30:43 +02:00
Benjamin Canac
b21c55f5c4 chore(Dropdown): move resolve class logic into template 2023-05-09 14:30:43 +02:00
Benjamin Canac
fc11612a49 chore(SelectMenu): move resolve class logic into template 2023-05-09 14:30:05 +02:00
Benjamin Canac
6de57aa1a0 chore(deps): bump 2023-05-09 11:50:28 +02:00
Benjamin Canac
6355b16156 chore(Button): add flex-shrink-0 by default 2023-05-05 18:17:43 +02:00
Benjamin Canac
26fc923ea4 chore(Input): add placeholder color to none appearance 2023-05-05 18:17:32 +02:00
Benjamin Canac
28ee9179f5 fix(VerticalNavigation): improve stacking context 2023-05-05 18:17:13 +02:00
Benjamin Canac
4665563e6f fix(CommandPalette): wrong type usage 2023-05-05 17:19:39 +02:00
Benjamin Canac
f221b890a9 chore(Card): update ring and divide colors 2023-05-05 17:17:46 +02:00
Benjamin Canac
d1d8ab3c64 fix(Button): variant validator takes color into account 2023-05-05 14:38:03 +02:00
Benjamin Canac
5b8ab168ba chore(app.config): remove commented button gray variant 2023-05-05 12:16:18 +02:00
Benjamin Canac
767a2bf3fc chore(app.config): improve modal and slideover overlay background 2023-05-05 12:15:59 +02:00
Benjamin Canac
0c69385771 fix: prefix imported components 2023-05-05 12:15:20 +02:00
Daniel Roe
97b1a85ea1 fix: revert back to runtime app for hmr (#153) 2023-05-04 18:18:36 +02:00
Benjamin Canac
9ce43ac68b fix(Notifications): missing computed from vue 2023-05-04 17:59:10 +02:00
Benjamin Canac
fa05653f23 fix(Select): move types from template 2023-05-04 17:51:23 +02:00
Benjamin Canac
626409e101 fix: put back app.config for hmr 2023-05-04 17:51:08 +02:00
Daniel Roe
f5c0030a19 fix: remove augmentation of app (#152) 2023-05-04 17:32:25 +02:00
Daniel Roe
11e00a10e4 fix: update to fix type issues (#151) 2023-05-04 17:24:22 +02:00
Benjamin Canac
b55a7c58f6 docs: improve installation description 2023-05-04 16:02:26 +02:00
Benjamin Canac
8c8bc0b751 docs: hide unfinished pages 2023-05-04 16:00:20 +02:00
Benjamin Canac
a076cae4bf fix(module): remove .ts ext from app.config 2023-05-04 15:38:32 +02:00
Benjamin Canac
5facfee76c Revert "chore(package): volta pin node 18"
This reverts commit a38ef00fb8.
2023-05-04 14:59:45 +02:00
Benjamin Canac
3c5256c462 chore(release): 2.0.0 2023-05-04 14:56:33 +02:00
Benjamin Canac
a38ef00fb8 chore(package): volta pin node 18 2023-05-04 14:55:18 +02:00
Benjamin Canac
6da0db0113 feat: rewrite to use app config and rework docs (#143)
Co-authored-by: Daniel Roe <daniel@roe.dev>
Co-authored-by: Sébastien Chopin <seb@nuxt.com>
2023-05-04 14:49:19 +02:00
Benjamin Canac
56230ea915 chore(release): 1.2.11 2023-05-04 11:01:28 +02:00
Benjamin Canac
126b5fcfd4 Revert "chore(github): add v1 branch to ci dev"
This reverts commit d3536d8768.
2023-05-04 11:00:12 +02:00
Benjamin Canac
d3536d8768 chore(github): add v1 branch to ci dev 2023-05-04 10:52:54 +02:00
Sylvain Marroufin
59f62d322f fix(defineShortcuts): use useEventListener (#150) 2023-05-04 10:50:10 +02:00
Sylvain Marroufin
b85a8e7203 chore(defineShortcuts): config prop whenever more flexible (#149) 2023-05-02 14:47:31 +02:00
Benjamin Canac
8830d848fd chore(release): 1.2.10 2023-04-07 19:23:36 +02:00
Benjamin Canac
cfce1524b2 fix(CommandPalette): typecheck 2023-04-07 19:23:25 +02:00
Benjamin Canac
f845e89a76 chore(release): 1.2.9 2023-04-07 19:21:55 +02:00
Benjamin Canac
d9ca5d188a chore(CommandPalette): improve command highlight 2023-04-07 18:30:25 +02:00
Benjamin Canac
2429bcf5a7 chore(SelectCustom): right padding only when selected 2023-04-05 13:01:16 +02:00
Benjamin Canac
f45f4a3e56 chore(release): 1.2.8 2023-04-04 15:14:06 +02:00
Benjamin Canac
09e957e702 chore(deps): remove @tailwindcss/line-clamp as its included by default in tailwind 3.3 2023-04-04 13:55:38 +02:00
Benjamin Canac
1ecd7cefde chore(release): 1.2.7 2023-04-04 13:36:43 +02:00
Benjamin Canac
aafdfdb59c fix(useTimer): remaining after pause 2023-04-04 13:36:24 +02:00
Benjamin Canac
453ff6ca20 chore(release): 1.2.6 2023-04-04 11:26:00 +02:00
Benjamin Canac
55832b6b99 docs: ts ignore 2023-04-04 11:25:33 +02:00
Benjamin Canac
6b93bbe5cd chore(release): 1.2.5 2023-04-04 11:17:30 +02:00
Benjamin Canac
1402553145 chore(deps): bump 2023-04-04 11:17:14 +02:00
Benjamin Canac
5d84dfd05b chore(release): 1.2.4 2023-04-04 11:08:58 +02:00
Benjamin Canac
7dc59a05ec chore(useTimer): pass options to useTimestamp 2023-04-04 11:08:41 +02:00
Benjamin Canac
4bd994985d chore(release): 1.2.3 2023-03-22 16:21:49 +01:00
Benjamin Canac
c83d3b7147 chore(Avatar): remove useless chipVariant prop 2023-03-22 16:21:29 +01:00
Benjamin Canac
f022665351 chore(release): 1.2.2 2023-03-20 16:17:55 +01:00
Benjamin Canac
f29c325dc7 chore(deps): fix @headlessui/vue to 1.7.10 because of inert dialogs 2023-03-20 16:17:36 +01:00
Benjamin Canac
876f9578c2 chore(release): 1.2.1 2023-03-20 15:59:15 +01:00
Benjamin Canac
f69f584188 chore(deps): bump 2023-03-20 15:58:53 +01:00
Sylvain Marroufin
377b4189ca fix(defineShortcuts): shift + alphabetic character handling (#140) 2023-03-13 14:11:52 +01:00
Benjamin Canac
f76a9f0ab0 chore(release): 1.2.0 2023-03-09 16:55:06 +01:00
Sylvain Marroufin
37b2271bf0 fix(defineShortcuts): add missing import 2023-03-09 16:09:31 +01:00
Sylvain Marroufin
fa49d52f17 fix(Tooltip): shortcutsClass prop type 2023-03-09 15:37:42 +01:00
Sylvain Marroufin
fd4b608150 chore: add more composables (#138) 2023-03-09 11:42:22 +01:00
Sylvain Marroufin
fef93f3198 chore: allow preset override of components shortcuts (#139)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2023-03-09 11:40:04 +01:00
Benjamin Canac
0826ef8d59 chore(deps): bump 2023-03-03 16:51:41 +01:00
Benjamin Canac
0e3066d865 chore(release): 1.1.4 2023-03-02 12:37:17 +01:00
Benjamin Canac
fb9d6cb544 chore(deps): freeze @headlessui/vue to 1.7.10 2023-03-02 12:36:55 +01:00
Benjamin Canac
531a89cdb8 chore(release): 1.1.3 2023-03-02 11:02:04 +01:00
Benjamin Canac
6970c2d665 chore(CommandPalette): add slot for command icon 2023-02-28 16:49:50 +01:00
Benjamin Canac
9719ea3858 chore(release): 1.1.2 2023-02-28 11:58:28 +01:00
Benjamin Canac
a4af6b3805 chore(deps): bump 2023-02-28 10:50:50 +01:00
Benjamin Canac
3493c138d9 chore: split props for Dropdown, ContextMenu and Popover 2023-02-24 17:33:57 +01:00
Benjamin Canac
d08e64d53f fix(Tooltip): truncate 2023-02-22 15:59:02 +01:00
renovate[bot]
6aecb082d2 chore(deps): update devdependency vue-tsc to ^1.1.7 (#132)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-22 12:31:21 +01:00
Sylvain Marroufin
63e27f8b4b chore(docs): add ContextMenu component page (#128) 2023-02-22 12:31:08 +01:00
Benjamin Canac
7970aefcb0 fix(VerticalNavigation): links to type 2023-02-22 12:22:54 +01:00
renovate[bot]
a893d7fa2e chore(deps): update all non-major dependencies (#124)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-21 19:26:03 +01:00
Benjamin Canac
8ace629ff8 chore(release): 1.1.1 2023-02-20 18:06:15 +01:00
Benjamin Canac
0d35b82ecb chore(CommandPalette): expose query 2023-02-20 18:05:52 +01:00
Benjamin Canac
5f37077835 types(CommandPalette): options no longer exists 2023-02-20 18:05:39 +01:00
Benjamin Canac
948f4b89b1 chore(release): 1.1.0 2023-02-17 19:08:09 +01:00
Benjamin Canac
e6d0dd5898 chore(CommandPalette): set debounce to 200 2023-02-17 19:07:38 +01:00
Benjamin Canac
4702a4f103 fix(CommandPalette): types 2023-02-17 18:14:11 +01:00
Benjamin Canac
efa9674815 feat(CommandPalette): handle async search for specific groups 2023-02-17 18:03:59 +01:00
Benjamin Canac
97d40395d3 chore(release): 1.0.0 2023-02-17 15:35:51 +01:00
Benjamin Canac
a2fb22d835 docs: ts-ignore module import 2023-02-17 14:53:35 +01:00
Benjamin Canac
d14a1a82c2 chore(deps): bump @egoist/tailwindcss-icons 2023-02-17 14:50:41 +01:00
Benjamin Canac
a566627a23 chore: bump deps 2023-02-17 14:26:20 +01:00
Benjamin Canac
ee3352278c feat: migrate to @egoist/tailwindcss-icons 2023-02-17 12:58:19 +01:00
Benjamin Canac
b1d9e01818 chore(release): 0.2.1 2023-02-16 17:02:23 +01:00
Benjamin Canac
ca171f3095 chore(ci): disable typecheck 2023-02-16 17:02:07 +01:00
Benjamin Canac
c0e493d96a chore(deps): bump nuxt-icon 2023-02-16 17:02:00 +01:00
Benjamin Canac
d0d3235860 chore(release): 0.2.0 2023-02-16 16:17:24 +01:00
Benjamin Canac
18915975be chore(release): 0.1.39 2023-02-16 16:16:57 +01:00
Benjamin Canac
f5d068be9d feat: use nuxt-icon 2023-02-16 16:16:37 +01:00
Benjamin Canac
6018f009a8 fix(SelectCustom): handle search on string arrays 2023-02-16 12:02:38 +01:00
Benjamin Canac
2b78b5d7dc chore(SelectCustom): improve options type 2023-02-16 11:53:19 +01:00
Benjamin Canac
87f3f0b4c0 chore(release): 0.1.38 2023-02-03 18:09:49 +01:00
Benjamin Canac
41bf56f2ae chore(tsconfig): remove override 2023-02-03 18:04:30 +01:00
Benjamin Canac
b7795f4ef6 chore(deps): add @types/node 2023-02-03 18:04:19 +01:00
Benjamin Canac
57f8145a8d chore(deps): bump 2023-02-03 17:51:06 +01:00
Benjamin Canac
70fbcb6b24 chore(release): 0.1.37 2023-02-03 16:02:21 +01:00
Kevin Marrec
bea47b5906 fix(CommandPalette): improve accessibility (#129) 2023-02-03 16:01:43 +01:00
Benjamin Canac
fc1b3b2f17 chore(release): 0.1.36 2023-02-02 15:10:25 +01:00
Benjamin Canac
5bf5a314c4 fix(CommandPalette): put back cursor on top only when query changes 2023-02-01 15:20:21 +01:00
Benjamin Canac
3558eb1a4f chore(release): 0.1.35 2023-02-01 14:38:15 +01:00
Benjamin Canac
1c4d46e056 fix(Dropdown): lint 2023-02-01 14:38:07 +01:00
Benjamin Canac
1b0ed9e732 docs: simplify popover panel template 2023-02-01 14:37:55 +01:00
Benjamin Canac
b72037a777 chore: handle disabled prop for Dropdown and Popover 2023-02-01 14:34:28 +01:00
Benjamin Canac
a7644860b8 fix(Dropdown): prevent panel display when no items 2023-01-31 12:33:27 +01:00
Benjamin Canac
c90cd9c4f3 fix(AvatarGroup): preset size prop 2023-01-28 01:49:24 +01:00
Benjamin Canac
7805168685 chore(release): 0.1.34 2023-01-27 14:10:46 +01:00
Benjamin Canac
27717a55b3 fix(CommandPalette): typecheck 2023-01-27 14:10:35 +01:00
Benjamin Canac
d651a22dce chore(release): 0.1.33 2023-01-27 13:02:18 +01:00
Benjamin Canac
c3ecbf4b20 chore(CommandPalette): start preset 2023-01-27 13:02:01 +01:00
211 changed files with 24878 additions and 13678 deletions

12
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,12 @@
module.exports = {
root: true,
extends: ['@nuxt/eslint-config'],
rules: {
'vue/multi-word-component-names': 0,
'vue/max-attributes-per-line': ['error', {
singleline: {
max: 5
}
}]
}
}

View File

@@ -1,9 +0,0 @@
module.exports = {
root: true,
extends: [
'@nuxtjs/eslint-config-typescript'
],
rules: {
'vue/multi-word-component-names': 0
}
}

31
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@@ -0,0 +1,31 @@
---
name: Bug report
about: Report a bug report to help us improve the module.
title: ''
labels: 'bug'
assignees: ''
---
<!-- **IMPORTANT!**
Before reporting a bug, please make sure that you have read through our documentation and you think your problem is indeed an issue related to our module. -->
### Version
@nuxthq/ui: <!-- ex: v2.0.0 -->
nuxt: <!-- ex: v3.5.0 -->
### Reproduction Link
<!--
A minimal test case based on one of:
- a GitHub repository that can reproduce the bug
- https://stackblitz.com/edit/nuxtlabs-ui
-->
### Steps to reproduce
### What is Expected?
### What is actually happening?

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Nuxt Community Discord
url: https://discord.nuxtjs.org/
about: Consider asking questions about the module here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea or enhancement for the module.
title: ''
labels: 'enhancement'
assignees: ''
---
### Is your feature request related to a problem? Please describe.
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
### Describe the solution you'd like
<!-- A clear and concise description of what you want to happen. -->
### Describe alternatives you've considered
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
### Additional context
<!-- Add any other context or screenshots about the feature request here. -->

16
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,16 @@
---
name: Question
about: Ask a question about the module.
title: ''
labels: 'question'
assignees: ''
---
<!-- **IMPORTANT!**
Please make sure to look for an answer to your question in our documentation and the documentation before asking a question here.
If you have a general question regarding the module use Discord `modules` channel. Thanks!
Nuxt Discord: https://discord.nuxtjs.org/
-->

View File

@@ -15,37 +15,48 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest] # macos-latest, windows-latest
node: [16]
node: [18]
steps:
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- name: Checkout
- name: checkout
uses: actions/checkout@master
with:
persist-credentials: false
fetch-depth: 0
- name: Cache
uses: actions/cache@v2
- uses: pnpm/action-setup@v2
name: Install pnpm
id: pnpm-install
with:
path: node_modules
key: ${{ matrix.os }}-node-v${{ matrix.node }}-deps-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
version: 7
run_install: false
- name: Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: yarn
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Lint
run: yarn lint
run: pnpm run lint
- name: Typecheck
run: yarn typecheck
run: pnpm run typecheck
- name: Build
run: yarn build
run: pnpm run build
- name: Release Edge
if: github.event_name == 'push'

View File

@@ -15,37 +15,48 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest] # macos-latest, windows-latest
node: [16]
node: [18]
steps:
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- name: Checkout
- name: checkout
uses: actions/checkout@master
with:
persist-credentials: false
fetch-depth: 0
- name: Cache
uses: actions/cache@v2
- uses: pnpm/action-setup@v2
name: Install pnpm
id: pnpm-install
with:
path: node_modules
key: ${{ matrix.os }}-node-v${{ matrix.node }}-deps-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
version: 7
run_install: false
- name: Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: yarn
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Lint
run: yarn lint
run: pnpm run lint
- name: Typecheck
run: yarn typecheck
run: pnpm run typecheck
- name: Build
run: yarn build
run: pnpm run build
- name: Version Check
id: check

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ nuxt.d.ts
dist
.DS_Store
.history
.vercel

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
shamefully-hoist=true

22
.release-it.json Normal file
View File

@@ -0,0 +1,22 @@
{
"git": {
"commitMessage": "chore(release): ${version}"
},
"npm": {
"publish": false
},
"github": {
"release": true,
"web": true
},
"hooks": {
"before:init": ["pnpm lint"]
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "conventionalcommits",
"infile": "CHANGELOG.md",
"ignoreRecommendedBump": true
}
}
}

View File

@@ -1,7 +1,326 @@
### [2.4.1](https://github.com/nuxtlabs/ui/compare/v2.4.0...v2.4.1) (2023-06-21)
### Bug Fixes
* **forms:** precise type assertion for `onInput` event handler ([#293](https://github.com/nuxtlabs/ui/issues/293)) ([457b7a9](https://github.com/nuxtlabs/ui/commit/457b7a9fb72e6469014b6ca18e7034dd5c6f44b8))
* **module:** let `tailwindcss` viewer enabled by default ([4023fbe](https://github.com/nuxtlabs/ui/commit/4023fbec29e5b4d40fd23e8c2ae3d0cf23addc64)), closes [#292](https://github.com/nuxtlabs/ui/issues/292)
* **module:** safelist aliases for input ([f719111](https://github.com/nuxtlabs/ui/commit/f719111abb94c81f3932927a0154b3e1bed73a9a))
* **module:** safelist regex when a `:` was present before color ([f7e2082](https://github.com/nuxtlabs/ui/commit/f7e2082983c2eb650e95a9040aafde4ce2c88c54))
* **Radio/Checkbox:** remove legacy `custom` ([3bac087](https://github.com/nuxtlabs/ui/commit/3bac0874f106a8ff7436b541f9d064c1c7c27464))
# Changelog
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [2.4.0](https://github.com/nuxtlabs/ui/compare/v2.3.0...v2.4.0) (2023-06-13)
### ⚠ BREAKING CHANGES
* **forms:** bind `$attrs` to elements (#279)
* **Select:** rename `text-attribute` to `option-attribute` and defaults to `label`
### Features
* **CommandPalette:** handle `empty-state` ([#271](https://github.com/nuxtlabs/ui/issues/271)) ([652af93](https://github.com/nuxtlabs/ui/commit/652af93f5c7cd4b34044a5597f3c14441ed6d998))
* **module:** smart safelisting ([#268](https://github.com/nuxtlabs/ui/issues/268)) ([20fa4d2](https://github.com/nuxtlabs/ui/commit/20fa4d2317fc1e14fe87fa273957b92e63668945))
* **Pagination:** new component ([#257](https://github.com/nuxtlabs/ui/issues/257)) ([f0b24ba](https://github.com/nuxtlabs/ui/commit/f0b24ba25d52184b8683e364016ed8fb800fc96b))
* **table:** add loading state ([#259](https://github.com/nuxtlabs/ui/issues/259)) ([4741532](https://github.com/nuxtlabs/ui/commit/47415322ea56b5388e55c404c901531e807a9f00))
* **table:** add slot for empty state ([#260](https://github.com/nuxtlabs/ui/issues/260)) ([f7a34c8](https://github.com/nuxtlabs/ui/commit/f7a34c8feeda6a4e1e1daff87b37b375aaa0c90d))
### Bug Fixes
* **ButtonGroup:** invalid `size` validator ([a617672](https://github.com/nuxtlabs/ui/commit/a6176720c75b26768ba91efcab50689a932931ad))
* **ButtonGroup:** use `-space-x-px` on wrapper ([d91c0bb](https://github.com/nuxtlabs/ui/commit/d91c0bb8944224d4e8eb62f99a33a6be94e5cd92))
* **Button:** same size when no label + uniformize form elements ([a6903df](https://github.com/nuxtlabs/ui/commit/a6903df58fb91da44e6f83cc2bd9c963827fe5dd))
* **CommandPalette:** input focus after be5f352 ([cbc8ef1](https://github.com/nuxtlabs/ui/commit/cbc8ef13cc3253690c22c32d90ea9746970c345a))
* **deps:** move `@tailwindcss/container-queries` to dependencies ([9559d0b](https://github.com/nuxtlabs/ui/commit/9559d0b3bc09956d7fe17ee0deeef03599d02d45))
* **forms:** `padded` prop with `p-0` class ([207444f](https://github.com/nuxtlabs/ui/commit/207444fdea773b8ee64dd4f80b4f70b76462a9d6))
* **forms:** bind `$attrs` to elements ([#279](https://github.com/nuxtlabs/ui/issues/279)) ([e12e974](https://github.com/nuxtlabs/ui/commit/e12e9740c97b75d3b7b70c38978e249b5e26eead))
* **module:** deduplicate default safelist as components may share same rules ([2cfa1f8](https://github.com/nuxtlabs/ui/commit/2cfa1f8d0355d4c9cec5d4294d63e043d223cd64))
* **module:** hardcode `gray` safelist instead of deduplicate complex logic ([a733c13](https://github.com/nuxtlabs/ui/commit/a733c13866cdb74398f3e6f022cc63223e269e19))
* **module:** only safelist known colors ([cdce519](https://github.com/nuxtlabs/ui/commit/cdce519742b86ff29460aa50264d7bb34ad24bd0))
* **module:** prevent safelisting dynamic `:color` variables ([ccd9ca5](https://github.com/nuxtlabs/ui/commit/ccd9ca5106d0b81aed6591097f121eb81dcc9b47))
* **module:** transform `vue` files to detect multi-line components ([88c1930](https://github.com/nuxtlabs/ui/commit/88c1930845d26c66c2fbd32f99f52dbd23244341))
* **module:** use `@tailwindcss/forms` class strategy ([#278](https://github.com/nuxtlabs/ui/issues/278)) ([be5f352](https://github.com/nuxtlabs/ui/commit/be5f352296cf4e0c9099cf468ed905283b31007d))
* **Notification:** class priority for icon color ([07f7855](https://github.com/nuxtlabs/ui/commit/07f7855a263e516250f62d0730afc69753d0322c))
* **Radio/Checkbox:** split preset as `indeterminate` is checkbox only ([429791d](https://github.com/nuxtlabs/ui/commit/429791dab0fbb84bae1e1e13e7e688708f0b5c98))
* **SelectMenu:** input focus after `be5f352` ([717a514](https://github.com/nuxtlabs/ui/commit/717a5144511c4db013a57869ac06421accf51e38))
* **Table:** colspan of `empty` and `loading` is wrong when selection enabled ([#284](https://github.com/nuxtlabs/ui/issues/284)) ([786d776](https://github.com/nuxtlabs/ui/commit/786d7765f5517a7e8cdd718ce93fd9fecc427ba7))
* **Toggle:** missing `disabled` prop ([fe833eb](https://github.com/nuxtlabs/ui/commit/fe833eb2b2b4d1d32eb9e082b437a0259b6f75c6))
* **Select:** rename `text-attribute` to `option-attribute` and defaults to `label` ([b4a96a8](https://github.com/nuxtlabs/ui/commit/b4a96a8b01b52751c9a9c6609ed8cf7ccf516a04))
## [2.3.0](https://github.com/nuxtlabs/ui/compare/v2.2.1...v2.3.0) (2023-06-05)
### ⚠ BREAKING CHANGES
* **Input:** move pointer class inside its own preset class
* **SelectMenu:** remove `inline-flex` from wrapper to behave like other form elements
* **Notification:** rename to `closeButton` and `actionButton` for consistency
* **CommandPalette:** rename props to `emptyState` and `closeButton` for consistency
* **Toggle:** rename icons to `onIcon` / `offIcon` for consistency
### Features
* add `Table` component ([#237](https://github.com/nuxtlabs/ui/issues/237)) ([cce000a](https://github.com/nuxtlabs/ui/commit/cce000ab2b2af1079216e0e79769703fc4d9933e))
### Bug Fixes
* **Avatar:** placeholder font size ([71edb91](https://github.com/nuxtlabs/ui/commit/71edb91c4ff17a258d6229ed6c6fa6a4b54bdd53))
* **Badge:** remove `console.log` in validator ([f9b935f](https://github.com/nuxtlabs/ui/commit/f9b935f5f59b872fd952a2739d305d6574bf7cf8))
* **Button:** invalid padding when using `square` prop ([1ebaa5a](https://github.com/nuxtlabs/ui/commit/1ebaa5aa00752cd276f7c754d64ac7f85b14dc26))
* **CommandPalette:** override of `closeButton` and `emptyState` props ([2c673f5](https://github.com/nuxtlabs/ui/commit/2c673f5377dbbcdefa6b57eddba2c19d065d5f1f))
* **defineShortcuts:** err with input autocomplete that triggers `keydown` ([01fa85c](https://github.com/nuxtlabs/ui/commit/01fa85c7a3e476d4f710ed3a36c1e815fc986a94))
* **SelectMenu:** disable on loading ([8951923](https://github.com/nuxtlabs/ui/commit/8951923a11d533ebf53dbec5f852800555af253c))
* **Table:** add missing `text-left` in `th.base` ([6bd5142](https://github.com/nuxtlabs/ui/commit/6bd5142a377694599952e0f9b53fde0d0132b61b))
* **Table:** missing `ref` import from `vue` ([272af9d](https://github.com/nuxtlabs/ui/commit/272af9d24c7cda8341e66b57f76acdb9f46ea23e))
* **Table:** override of `sortButton` and `emptyState` props ([192b0e6](https://github.com/nuxtlabs/ui/commit/192b0e63018ae73e8acaa8b4b1771cda2b59bdb6))
* **Table:** type `sort` prop ([3ba0aed](https://github.com/nuxtlabs/ui/commit/3ba0aedcba578350e2fdd9c180505ed8920e0404))
* use `cloneVNode` when altering props in render functions ([5e50eb9](https://github.com/nuxtlabs/ui/commit/5e50eb9eb82571d22e0a2f1a2fe985addf7efe18)), closes [#252](https://github.com/nuxtlabs/ui/issues/252)
* **CommandPalette:** rename props to `emptyState` and `closeButton` for consistency ([daca463](https://github.com/nuxtlabs/ui/commit/daca46371cab1344bd87ffb0abe0f7e9cdb08609))
* **Input:** move pointer class inside its own preset class ([f59a92c](https://github.com/nuxtlabs/ui/commit/f59a92ca1533a44e17fbc8b7945bdaa9a83e805a))
* **Notification:** rename to `closeButton` and `actionButton` for consistency ([4458656](https://github.com/nuxtlabs/ui/commit/4458656be5547fc9505a5c4758bea4818ada408b))
* **SelectMenu:** remove `inline-flex` from wrapper to behave like other form elements ([ba44c58](https://github.com/nuxtlabs/ui/commit/ba44c58a80252a4394fcf2f84611ea2696883120))
* **Toggle:** rename icons to `onIcon` / `offIcon` for consistency ([8ee2ac1](https://github.com/nuxtlabs/ui/commit/8ee2ac10e7eda4c54418f613a5ef87dd89e1f7eb))
### [2.2.1](https://github.com/nuxtlabs/ui/compare/v2.2.0...v2.2.1) (2023-05-27)
### Bug Fixes
* **FormGroup:** missing `h` import from `vue` ([a96dc19](https://github.com/nuxtlabs/ui/commit/a96dc192157725143503b1a5e4b404cb48dc9d3f)), closes [#236](https://github.com/nuxtlabs/ui/issues/236)
## [2.2.0](https://github.com/nuxtlabs/ui/compare/v2.1.0...v2.2.0) (2023-05-26)
### ⚠ BREAKING CHANGES
* handle color states on form elements (#234)
* **Notification:** rename `progressColor` to `color` and style icon
* **Avatar:** remove `chipVariant` prop
* **VerticalNavigation:** split preset
### Features
* handle color states on form elements ([#234](https://github.com/nuxtlabs/ui/issues/234)) ([9ce531a](https://github.com/nuxtlabs/ui/commit/9ce531a06f1a972bc003876162e0503c1bbbdbd8))
### Bug Fixes
* **Notification:** remove default color on icon ([1a9dc5c](https://github.com/nuxtlabs/ui/commit/1a9dc5c980d8477cdf9386a17e20fc9fec0d883e))
* **Radio/Checkbox:** remove ring offset on focus ([a56dbea](https://github.com/nuxtlabs/ui/commit/a56dbeab351a5c58e5bb49f5762669e2884c6483))
* **VerticalNavigation:** badge display ([d2ee505](https://github.com/nuxtlabs/ui/commit/d2ee5058f819fc17f281f323dab2f0b3d80cf7bd)), closes [#205](https://github.com/nuxtlabs/ui/issues/205)
* **Avatar:** remove `chipVariant` prop ([1f22f84](https://github.com/nuxtlabs/ui/commit/1f22f84360c20498eea8971b21db9293a4c9c3dc))
* **Notification:** rename `progressColor` to `color` and style icon ([1b61ec7](https://github.com/nuxtlabs/ui/commit/1b61ec72e292325d7776a4719f14a75bdb18e110))
* **VerticalNavigation:** split preset ([19923cb](https://github.com/nuxtlabs/ui/commit/19923cbf1edc6c6d4aefb9ffab9f908b116e1c69))
## [2.1.0](https://github.com/nuxtlabs/ui/compare/v2.0.4...v2.1.0) (2023-05-19)
### Bug Fixes
* **app.config:** trailing space ([703fdef](https://github.com/nuxtlabs/ui/commit/703fdef9bd4c0e26b0e38a13c30aff5b1d9d19aa))
* **ButtonGroup/AvatarGroup:** allow `v-for` ([#173](https://github.com/nuxtlabs/ui/issues/173)) ([3fa10aa](https://github.com/nuxtlabs/ui/commit/3fa10aa4ebf9ff7d443f8f2564dcaf9b63ce1fa8))
* **DocsPageHeader:** github component link ([#182](https://github.com/nuxtlabs/ui/issues/182)) ([7f00ec6](https://github.com/nuxtlabs/ui/commit/7f00ec6c3d059e5e78172a8e4bab905a7f02fa63))
* **Input:** expose ref ([2ded24b](https://github.com/nuxtlabs/ui/commit/2ded24bec90a5ea6665ab6895ced15d9dd49e8ef))
* **module:** add `.mjs` extension to tailwind `content` when builded ([246449b](https://github.com/nuxtlabs/ui/commit/246449b32850db805c1133151b8449687e7c14be)), closes [#172](https://github.com/nuxtlabs/ui/issues/172)
* **Textarea:** expose ref ([ea740bf](https://github.com/nuxtlabs/ui/commit/ea740bf10a6090545ed58ff26322ee3a679b5452))
### [2.0.4](https://github.com/nuxtlabs/ui/compare/v2.0.3...v2.0.4) (2023-05-15)
### Bug Fixes
* **SelectMenu:** add missing `inline-flex` on wrapper ([e8b4654](https://github.com/nuxtlabs/ui/commit/e8b46540d8767c7a63c0ff8e28263615626916e7))
### [2.0.3](https://github.com/nuxtlabs/ui/compare/v2.0.2...v2.0.3) (2023-05-15)
### [2.0.2](https://github.com/nuxtlabs/ui/compare/v2.0.1...v2.0.2) (2023-05-11)
### Bug Fixes
* **LinkCustom:** handle `button` when no `to` prop ([c7c78cb](https://github.com/nuxtlabs/ui/commit/c7c78cb47b00963c8a9ea0c0599fbc7e128cff66))
### [2.0.1](https://github.com/nuxtlabs/ui/compare/v2.0.0...v2.0.1) (2023-05-11)
### Bug Fixes
* **app.config:** remove old `u-` classes ([939efba](https://github.com/nuxtlabs/ui/commit/939efba47ceb660e5448a3ea42f2acd71b9837ee))
* **Avatar:** `gray` missing for `chipColor` ([fd4c80a](https://github.com/nuxtlabs/ui/commit/fd4c80acd4c70c7d378ebf780cd843115d8f434d))
* **Avatar:** shrink chip ring ([ebf5fd6](https://github.com/nuxtlabs/ui/commit/ebf5fd6aeb2a5363e80457cf8245fbab5fbc17ca))
* **Button:** `variant` validator takes color into account ([d1d8ab3](https://github.com/nuxtlabs/ui/commit/d1d8ab3c647d50f37832d1ae531550944d5aa8e3))
* **colors:** missing `useNuxtApp` import ([76a0d61](https://github.com/nuxtlabs/ui/commit/76a0d61a0f7b3936b0eceff16e17bc6540fb946c))
* **CommandPalette:** expose input ref to template ([192bf4c](https://github.com/nuxtlabs/ui/commit/192bf4c375293b16d952b94cc098a0260f47996a))
* **CommandPalette:** put back searchable on `v-show` to input ref always exists ([aacb7e9](https://github.com/nuxtlabs/ui/commit/aacb7e98412d2973c6fc61d9cb3b6da9bd433eb0))
* **CommandPalette:** wrong type usage ([4665563](https://github.com/nuxtlabs/ui/commit/4665563e6f9c4054cb1c859991369fe2cc844047))
* **docs:** sticky search button `z-index` ([f48ead6](https://github.com/nuxtlabs/ui/commit/f48ead6faf6fd14deeff84ca7b25d6bb7fae6f12))
* **Icon:** missing import ([cd430a4](https://github.com/nuxtlabs/ui/commit/cd430a4cad5143c5bd45c003086091f769e4f015))
* **module:** remove `.ts` ext from app.config ([a076cae](https://github.com/nuxtlabs/ui/commit/a076cae4bfa387e1fd9800741b10702896c21ad2))
* **Notifications:** missing `computed` from vue ([9ce43ac](https://github.com/nuxtlabs/ui/commit/9ce43ac68bcef3fb7fff8a9e317ad6d4a5ac2cb9))
* prefix imported components ([0c69385](https://github.com/nuxtlabs/ui/commit/0c69385771ff1815cdcbff812962056da381a541))
* put back app.config for hmr ([626409e](https://github.com/nuxtlabs/ui/commit/626409e1014ddcacaf6ee155830bd9862b335058))
* remove augmentation of app ([#152](https://github.com/nuxtlabs/ui/issues/152)) ([f5c0030](https://github.com/nuxtlabs/ui/commit/f5c0030a198579e5929fd517b80e2e20c9bac769))
* revert back to runtime app for hmr ([#153](https://github.com/nuxtlabs/ui/issues/153)) ([97b1a85](https://github.com/nuxtlabs/ui/commit/97b1a85ea12499289866a6baf15661c1f15279ce))
* **Select:** move types from template ([fa05653](https://github.com/nuxtlabs/ui/commit/fa05653f23c4e9b1732eb4b9cd5e034f9bdca272))
* **Toggle:** wrong `icon-off` positioning ([d5471f4](https://github.com/nuxtlabs/ui/commit/d5471f4d371b72df0ca5fac36e698066aca3864e))
* update to fix type issues ([#151](https://github.com/nuxtlabs/ui/issues/151)) ([11e00a1](https://github.com/nuxtlabs/ui/commit/11e00a10e4781881e293e5fcd382331008c15346))
* **VerticalNavigation:** improve focus ([034a95d](https://github.com/nuxtlabs/ui/commit/034a95d3c92eee9a54bd266e02d7446f7792d051))
* **VerticalNavigation:** improve stacking context ([28ee917](https://github.com/nuxtlabs/ui/commit/28ee9179f5fbc006a47719ee632adf54f0e0ec4d))
## [2.0.0](https://github.com/nuxtlabs/ui/compare/v1.2.11...v2.0.0) (2023-05-04)
### Features
* rewrite to use app config and rework docs ([#143](https://github.com/nuxtlabs/ui/issues/143)) ([6da0db0](https://github.com/nuxtlabs/ui/commit/6da0db0113733df1a03220cb528bea862b553f37))
### [1.2.11](https://github.com/nuxtlabs/ui/compare/v1.2.10...v1.2.11) (2023-05-04)
### Bug Fixes
* **defineShortcuts:** use `useEventListener` ([#150](https://github.com/nuxtlabs/ui/issues/150)) ([59f62d3](https://github.com/nuxtlabs/ui/commit/59f62d322f07919d16a8d35340c3aa038cd09520))
### [1.2.10](https://github.com/nuxtlabs/ui/compare/v1.2.9...v1.2.10) (2023-04-07)
### Bug Fixes
* **CommandPalette:** typecheck ([cfce152](https://github.com/nuxtlabs/ui/commit/cfce1524b209212d9ce635b61376ff0d6bc3601b))
### [1.2.9](https://github.com/nuxtlabs/ui/compare/v1.2.8...v1.2.9) (2023-04-07)
### [1.2.8](https://github.com/nuxtlabs/ui/compare/v1.2.7...v1.2.8) (2023-04-04)
### [1.2.7](https://github.com/nuxtlabs/ui/compare/v1.2.6...v1.2.7) (2023-04-04)
### Bug Fixes
* **useTimer:** remaining after pause ([aafdfdb](https://github.com/nuxtlabs/ui/commit/aafdfdb59c365c542f93703dd52b4306ac935040))
### [1.2.6](https://github.com/nuxtlabs/ui/compare/v1.2.5...v1.2.6) (2023-04-04)
### [1.2.5](https://github.com/nuxtlabs/ui/compare/v1.2.4...v1.2.5) (2023-04-04)
### [1.2.4](https://github.com/nuxtlabs/ui/compare/v1.2.3...v1.2.4) (2023-04-04)
### [1.2.3](https://github.com/nuxtlabs/ui/compare/v1.2.2...v1.2.3) (2023-03-22)
### [1.2.2](https://github.com/nuxtlabs/ui/compare/v1.2.1...v1.2.2) (2023-03-20)
### [1.2.1](https://github.com/nuxtlabs/ui/compare/v1.2.0...v1.2.1) (2023-03-20)
### Bug Fixes
* **defineShortcuts:** shift + alphabetic character handling ([#140](https://github.com/nuxtlabs/ui/issues/140)) ([377b418](https://github.com/nuxtlabs/ui/commit/377b4189ca85603db0b7f040949260ba7494c46f))
## [1.2.0](https://github.com/nuxtlabs/ui/compare/v1.1.4...v1.2.0) (2023-03-09)
### Bug Fixes
* **defineShortcuts:** add missing import ([37b2271](https://github.com/nuxtlabs/ui/commit/37b2271bf04adfe6bee4d984fa12452b2168318c))
* **Tooltip:** `shortcutsClass` prop type ([fa49d52](https://github.com/nuxtlabs/ui/commit/fa49d52f17752eaa06f997a9b6e8df8adcab983f))
### [1.1.4](https://github.com/nuxtlabs/ui/compare/v1.1.3...v1.1.4) (2023-03-02)
### [1.1.3](https://github.com/nuxtlabs/ui/compare/v1.1.2...v1.1.3) (2023-03-02)
### [1.1.2](https://github.com/nuxtlabs/ui/compare/v1.1.1...v1.1.2) (2023-02-28)
### Bug Fixes
* **Tooltip:** truncate ([d08e64d](https://github.com/nuxtlabs/ui/commit/d08e64d53fa439f34d51909bcb6812f1bcd95d83))
* **VerticalNavigation:** links `to` type ([7970aef](https://github.com/nuxtlabs/ui/commit/7970aefcb032ce01fcb11e9285fa61ce87f59519))
### [1.1.1](https://github.com/nuxtlabs/ui/compare/v1.1.0...v1.1.1) (2023-02-20)
## [1.1.0](https://github.com/nuxtlabs/ui/compare/v1.0.0...v1.1.0) (2023-02-17)
### Features
* **CommandPalette:** handle async search for specific groups ([efa9674](https://github.com/nuxtlabs/ui/commit/efa9674815ab4de756079690da0a381c3703d564))
### Bug Fixes
* **CommandPalette:** types ([4702a4f](https://github.com/nuxtlabs/ui/commit/4702a4f10379201c167cc52099519778756a5780))
## [1.0.0](https://github.com/nuxtlabs/ui/compare/v0.2.1...v1.0.0) (2023-02-17)
### Features
* migrate to `@egoist/tailwindcss-icons` ([ee33522](https://github.com/nuxtlabs/ui/commit/ee3352278cf03fdd12f2a4663b403052de3f089a))
### [0.2.1](https://github.com/nuxtlabs/ui/compare/v0.2.0...v0.2.1) (2023-02-16)
## [0.2.0](https://github.com/nuxtlabs/ui/compare/v0.1.39...v0.2.0) (2023-02-16)
### [0.1.39](https://github.com/nuxtlabs/ui/compare/v0.1.38...v0.1.39) (2023-02-16)
### Features
* use `nuxt-icon` ([f5d068b](https://github.com/nuxtlabs/ui/commit/f5d068be9d5778b3d4fcdc11d06d9d765e62075d))
### Bug Fixes
* **SelectCustom:** handle search on string arrays ([6018f00](https://github.com/nuxtlabs/ui/commit/6018f009a86cca196d15e4e72dd5eb41aaeb4bad))
### [0.1.38](https://github.com/nuxtlabs/ui/compare/v0.1.37...v0.1.38) (2023-02-03)
### [0.1.37](https://github.com/nuxtlabs/ui/compare/v0.1.36...v0.1.37) (2023-02-03)
### Bug Fixes
* **CommandPalette:** improve accessibility ([#129](https://github.com/nuxtlabs/ui/issues/129)) ([bea47b5](https://github.com/nuxtlabs/ui/commit/bea47b5906d1bc665717830d6dc2f3ff2a0374f3))
### [0.1.36](https://github.com/nuxtlabs/ui/compare/v0.1.35...v0.1.36) (2023-02-02)
### Bug Fixes
* **CommandPalette:** put back cursor on top only when query changes ([5bf5a31](https://github.com/nuxtlabs/ui/commit/5bf5a314c414b96c656190719bd56acca10676f5))
### [0.1.35](https://github.com/nuxtlabs/ui/compare/v0.1.34...v0.1.35) (2023-02-01)
### Bug Fixes
* **AvatarGroup:** preset size prop ([c90cd9c](https://github.com/nuxtlabs/ui/commit/c90cd9c4f37bc3ce5f6e13f3279dc2c574c76524))
* **Dropdown:** lint ([1c4d46e](https://github.com/nuxtlabs/ui/commit/1c4d46e056adf84d69462a12af8ac29f93cbf87a))
* **Dropdown:** prevent panel display when no items ([a764486](https://github.com/nuxtlabs/ui/commit/a7644860b8c22a0163e01ca2c0eab2c48b09745a))
### [0.1.34](https://github.com/nuxtlabs/ui/compare/v0.1.33...v0.1.34) (2023-01-27)
### Bug Fixes
* **CommandPalette:** typecheck ([27717a5](https://github.com/nuxtlabs/ui/commit/27717a55b3e5120f32fba2bcea30f5a91262f1c5))
### [0.1.33](https://github.com/nuxtlabs/ui/compare/v0.1.32...v0.1.33) (2023-01-27)
### [0.1.32](https://github.com/nuxtlabs/ui/compare/v0.1.31...v0.1.32) (2023-01-23)
### [0.1.31](https://github.com/nuxtlabs/ui/compare/v0.1.30...v0.1.31) (2023-01-17)
@@ -313,4 +632,4 @@ All notable changes to this project will be documented in this file. See [standa
* **Toggle:** add missing `computed` import ([0f09c9b](https://github.com/nuxtlabs/ui/commit/0f09c9baae501458af029f853c78b1c10a3ac133))
* **Tooltip:** missing `ref` import ([b08a8cc](https://github.com/nuxtlabs/ui/commit/b08a8cc0ac79e89817e338281a81c477d5ec645a))
* **useTimer:** remove log ([c6dcbd1](https://github.com/nuxtlabs/ui/commit/c6dcbd1b2b542dab1850504a60451a485e2d4004))
* **VerticalNavigation:** add `v-if` on label ([79d8e08](https://github.com/nuxtlabs/ui/commit/79d8e086f0c61887c52da6fe4a13f1bdf7077227))
* **VerticalNavigation:** add `v-if` on label ([79d8e08](https://github.com/nuxtlabs/ui/commit/79d8e086f0c61887c52da6fe4a13f1bdf7077227))

9
LICENSE.md Normal file
View File

@@ -0,0 +1,9 @@
MIT License
Copyright (c) 2023 NuxtLabs
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,6 +1,22 @@
# @nuxthq/ui
# NuxtLabs UI
Components library as a Nuxt module using [TailwindCSS](https://tailwindcss.com).
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
[![Nuxt][nuxt-src]][nuxt-href]
This module has been developed by [NuxtLabs](https://nuxtlabs.com/) for [Volta](https://volta.net) and [Nuxt Studio](https://nuxt.studio/). It provides everything related to UI when building a Nuxt application, including components, icons, colors, dark mode and also keyboard shortcuts.
[![social preview](https://repository-images.githubusercontent.com/428329515/5a18c5dd-bb58-4874-b6ef-1c44e4884344)](https://ui.nuxtlabs.com)
## Features
- Built with [Headless UI](https://headlessui.dev/) and [Tailwind CSS](https://tailwindcss.com/)
- HMR support through Nuxt App Config
- Dark mode support
- Keyboard shortcuts
- Bundled icons
- Fully typed
## Installation
@@ -8,13 +24,11 @@ Components library as a Nuxt module using [TailwindCSS](https://tailwindcss.com)
yarn add --dev @nuxthq/ui
```
Then, register the module in your `nuxt.config.js`:
Then, register the module in your `nuxt.config.ts`:
```js
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
buildModules: [
modules: [
'@nuxthq/ui'
]
})
@@ -30,42 +44,33 @@ If you want latest updates, please use `@nuxthq/ui-edge` in your `package.json`:
}
```
## Options
## Documentation
- `primary`
Visit http://ui.nuxtlabs.com to view the documentation.
Define the primary variant. Defaults to `indigo`. You can specify your own object of colors like here:
## Credits
**Example:**
- [nuxt/nuxt](https://github.com/nuxt/nuxt)
- [nuxt-modules/color-mode](https://github.com/nuxt-modules/color-mode)
- [nuxt-modules/tailwindcss](https://github.com/nuxt-modules/tailwindcss)
- [tailwindlabs/tailwindcss](https://github.com/tailwindlabs/tailwindcss)
- [tailwindlabs/headlessui](https://github.com/tailwindlabs/headlessui)
- [vueuse/vueuse](https://github.com/vueuse/vueuse)
- [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons)
```js
import { defineNuxtConfig } from 'nuxt'
## License
export default defineNuxtConfig({
buildModules: [
'@nuxthq/ui'
],
ui: {
primary: 'blue'
}
})
```
Licensed under the [MIT license](https://github.com/nuxtlabs/ui/blob/dev/LICENSE.md).
- `prefix`
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/@nuxthq/ui/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-version-href]: https://npmjs.com/package/@nuxthq/ui
Define the prefix of the imported components. Defaults to `u`.
[npm-downloads-src]: https://img.shields.io/npm/dm/@nuxthq/ui.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-downloads-href]: https://npmjs.com/package/@nuxthq/ui
**Example:**
[license-src]: https://img.shields.io/github/license/nuxtlabs/ui.svg?style=flat&colorA=18181B&colorB=28CF8D
[license-href]: https://github.com/nuxtlabs/ui/blob/main/LICENSE
```js
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
buildModules: [
'@nuxthq/ui'
],
ui: {
prefix: 'tw'
}
})
```
[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
[nuxt-href]: https://nuxt.com

View File

@@ -1,12 +0,0 @@
import fs from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
hooks: {
'rollup:done': async (ctx) => {
// copy env.d.ts to dist
await fs.copyFile(resolve('src/env.d.ts'), join(ctx.options.outDir, 'env.d.ts'))
}
}
})

View File

@@ -1,95 +1,68 @@
<template>
<div>
<nav class="u-bg-white border-b u-border-gray-200 fixed top-0 inset-x-0 z-10">
<UContainer padded>
<div class="flex items-center justify-between h-16">
<div class="flex items-center">
<NuxtLink to="/" class="block font-bold text-lg u-text-gray-900">
@nuxthq/ui
</NuxtLink>
</div>
<Header />
<div class="flex items-center gap-3">
<ColorScheme placeholder="" tag="span">
<UButton variant="transparent" :icon="colorMode.value === 'dark' ? 'heroicons-outline:moon' : 'heroicons-outline:sun'" @click="toggleDark" />
</ColorScheme>
<UButton to="https://github.com/nuxtlabs/ui" target="_blank" variant="transparent" icon="fa-brands:github" />
</div>
</div>
</UContainer>
</nav>
<UContainer>
<div class="relative grid lg:grid-cols-10 lg:gap-8">
<DocsAside class="lg:col-span-2" />
<UContainer class="mt-16">
<div class="lg:grid lg:grid-cols-10 lg:gap-10 lg:relative">
<aside class="lg:flex lg:flex-col pb-8 lg:pb-0 lg:sticky lg:top-0 px-4 sm:px-6 lg:px-0 lg:pt-16 lg:-mt-16 lg:self-start lg:col-span-2 lg:overflow-hidden lg:h-screen">
<nav class="overflow-y-auto h-auto pt-8 lg:py-12">
<ul class="space-y-6">
<li v-for="section of sections" :key="section">
<h5 class="mb-3 uppercase tracking-wide font-semibold text-xs u-text-gray-900">
{{ section.label }}
</h5>
<ul class="space-y-1.5">
<li v-for="(link, index) of section.links" :key="index">
<ULink
:to="link.to"
class="relative block text-sm rounded-md"
active-class="text-primary-600"
inactive-class="u-text-gray-500 hover:u-text-gray-700"
exact
>
{{ link.label }}
</ULink>
</li>
</ul>
</li>
</ul>
</nav>
</aside>
<div class="relative pt-8 pb-16" :class="[toc ? 'lg:col-span-6' : 'lg:col-span-8']">
<DocsPageHeader />
<div class="space-y-6 sm:px-6 lg:px-0 lg:col-span-8 lg:py-12">
<NuxtPage />
<DocsPageFooter class="mt-12" />
<hr class="border-gray-200 dark:border-gray-800 my-6">
<DocsPrevNext />
<DocsFooter class="mt-16" />
</div>
<DocsToc v-if="toc" class="lg:col-span-2 order-first lg:order-last" />
</div>
</UContainer>
<ClientOnly>
<UNotifications />
<DocsSearch />
</ClientOnly>
<UNotifications />
</div>
</template>
<script setup>
<script setup lang="ts">
const { toc } = useContent()
const colorMode = useColorMode()
// Computed
const color = computed(() => colorMode.value === 'dark' ? '#18181b' : 'white')
// Head
useHead({
title: '@nuxthq/ui',
titleTemplate: title => title && title.includes('NuxtLabs UI') ? title : `${title} - NuxtLabs UI`,
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' },
{ key: 'theme-color', name: 'theme-color', content: color }
],
link: [
{ rel: 'stylesheet', href: 'https://rsms.me/inter/inter.css' }
{ rel: 'stylesheet', href: 'https://rsms.me/inter/inter.css' },
{ rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }
],
htmlAttrs: {
lang: 'en'
},
bodyAttrs: {
class: 'antialiased font-sans text-gray-700 bg-gray-50 dark:bg-gray-900 dark:text-gray-200 bg-white dark:bg-black'
class: 'antialiased font-sans text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-900'
}
})
const colorMode = useColorMode()
const toggleDark = () => {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
const sections = [
{ label: 'Getting Started', links: [{ label: 'Usage', to: '/' }, { label: 'Examples', to: '/examples' }, { label: 'Migration', to: '/migration' }, { label: 'Dark mode', to: '/dark' }] },
{ label: 'Elements', links: [{ label: 'Avatar', to: '/components/Avatar' }, { label: 'AvatarGroup', to: '/components/AvatarGroup' }, { label: 'Badge', to: '/components/Badge' }, { label: 'Button', to: '/components/Button' }, { label: 'Dropdown', to: '/components/Dropdown' }, { label: 'Icon', to: '/components/Icon' }] },
{ label: 'Feedback', links: [{ label: 'Alert', to: '/components/Alert' }, { label: 'AlertDialog', to: '/components/AlertDialog' }] },
{ label: 'Forms', links: [{ label: 'Checkbox', to: '/components/Checkbox' }, { label: 'Input', to: '/components/Input' }, { label: 'FormGroup', to: '/components/FormGroup' }, { label: 'Radio', to: '/components/Radio' }, { label: 'Select', to: '/components/Select' }, { label: 'SelectCustom', to: '/components/SelectCustom' }, { label: 'Textarea', to: '/components/Textarea' }, { label: 'Toggle', to: '/components/Toggle' }] },
{ label: 'Layout', links: [{ label: 'Card', to: '/components/Card' }, { label: 'Container', to: '/components/Container' }] },
{ label: 'Navigation', links: [{ label: 'Pills', to: '/components/Pills' }, { label: 'Tabs', to: '/components/Tabs' }, { label: 'VerticalNavigation', to: '/components/VerticalNavigation' }, { label: 'CommandPalette', to: '/components/CommandPalette' }] },
{ label: 'Overlays', links: [{ label: 'Modal', to: '/components/Modal' }, { label: 'Notification', to: '/components/Notification' }, { label: 'Popover', to: '/components/Popover' }, { label: 'Slideover', to: '/components/Slideover' }, { label: 'Tooltip', to: '/components/Tooltip' }] }
]
useSeoMeta({
ogImage: '/social-preview.jpg',
twitterImage: '/social-preview.jpg',
twitterCard: 'summary_large_image'
})
</script>
<style>
html.dark {
@apply bg-black;
}
</style>

View File

@@ -0,0 +1,48 @@
import type { RouterConfig } from '@nuxt/schema'
function findHashPosition (hash): { el: any, behavior: ScrollBehavior, top: number } {
const el = document.querySelector(hash)
// vue-router does not incorporate scroll-margin-top on its own.
if (el) {
const top = parseFloat(getComputedStyle(el).scrollMarginTop)
return {
el: hash,
behavior: 'smooth',
top
}
}
}
// https://router.vuejs.org/api/#routeroptions
export default <RouterConfig>{
scrollBehavior (to, from, savedPosition) {
const nuxtApp = useNuxtApp()
// If history back
if (savedPosition) {
// Handle Suspense resolution
return new Promise((resolve) => {
nuxtApp.hooks.hookOnce('page:finish', () => {
setTimeout(() => resolve(savedPosition), 50)
})
})
}
// Scroll to heading on click
if (to.hash) {
return new Promise((resolve) => {
if (to.path === from.path) {
setTimeout(() => resolve(findHashPosition(to.hash)), 50)
} else {
nuxtApp.hooks.hookOnce('page:finish', () => {
setTimeout(() => resolve(findHashPosition(to.hash)), 50)
})
}
})
}
// Scroll to top of window
return { top: 0 }
}
}

116
docs/components/Header.vue Normal file
View File

@@ -0,0 +1,116 @@
<template>
<header class="sticky top-0 z-50 w-full backdrop-blur flex-none border-b border-gray-900/10 dark:border-gray-50/[0.06] bg-white/75 dark:bg-gray-900/75">
<UContainer>
<div class="flex items-center justify-between h-16">
<div class="flex items-center gap-3">
<NuxtLink to="/getting-started" class="flex items-end gap-1.5 font-bold text-xl text-gray-900 dark:text-white">
<Logo class="w-8 h-8 text-primary-500 dark:text-primary-400" />
NuxtLabs<span class="text-primary-500 dark:text-primary-400">UI</span>
</NuxtLink>
</div>
<div class="flex items-center -mr-1.5 gap-1.5">
<div class="hidden lg:block">
<ThemeSelect />
</div>
<UButton
color="gray"
variant="ghost"
class="lg:hidden"
icon="i-heroicons-magnifying-glass-20-solid"
@click="openDocsSearch"
/>
<ClientOnly>
<UButton
:icon="isDark ? 'i-heroicons-moon' : 'i-heroicons-sun'"
color="gray"
variant="ghost"
aria-label="Theme"
@click="isDark = !isDark"
/>
<template #fallback>
<div class="w-8 h-8" />
</template>
</ClientOnly>
<UButton
to="https://github.com/nuxtlabs/ui"
target="_blank"
color="gray"
variant="ghost"
icon="i-simple-icons-github"
/>
<UButton
color="gray"
variant="ghost"
class="lg:hidden"
icon="i-heroicons-bars-3-20-solid"
@click="isDialogOpen = true"
/>
</div>
</div>
</UContainer>
<TransitionRoot :show="isDialogOpen" as="template">
<Dialog as="div" @close="isDialogOpen = false">
<DialogPanel class="fixed inset-0 z-50 overflow-y-auto bg-white dark:bg-gray-900 lg:hidden">
<div class="px-4 sm:px-6 sticky top-0 border-b border-gray-900/10 dark:border-gray-50/[0.06] bg-white/75 dark:bg-gray-900/75 backdrop-blur z-10">
<div class="flex items-center justify-between h-16">
<div class="flex items-center gap-3">
<NuxtLink to="/getting-started" class="flex items-end gap-1.5 font-bold text-xl text-gray-900 dark:text-white">
<Logo class="w-8 h-8 text-primary-500 dark:text-primary-400" />
NuxtLabs<span class="text-primary-500 dark:text-primary-400">UI</span>
</NuxtLink>
</div>
<div class="flex -mr-1.5">
<UButton
color="gray"
variant="ghost"
icon="i-heroicons-x-mark-20-solid"
@click="isDialogOpen = false"
/>
</div>
</div>
</div>
<div class="px-4 sm:px-6 py-4 sm:py-6">
<ThemeSelect class="mb-4 sm:mb-6 w-full" />
<DocsAsideLinks @click="isDialogOpen = false" />
</div>
</DialogPanel>
</Dialog>
</TransitionRoot>
</header>
</template>
<script setup lang="ts">
import { Dialog, DialogPanel, TransitionRoot } from '@headlessui/vue'
const { isSearchModalOpen } = useDocs()
const colorMode = useColorMode()
const isDialogOpen = ref(false)
const isDark = computed({
get () {
return colorMode.value === 'dark'
},
set () {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
function openDocsSearch () {
isDialogOpen.value = false
setTimeout(() => {
isSearchModalOpen.value = true
}, 100)
}
</script>

5
docs/components/Logo.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<svg width="900" height="900" viewBox="0 0 900 900" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M504.908 750H839.476C850.103 750.001 860.542 747.229 869.745 741.963C878.948 736.696 886.589 729.121 891.9 719.999C897.211 710.876 900.005 700.529 900 689.997C899.995 679.465 897.193 669.12 891.873 660.002L667.187 274.289C661.876 265.169 654.237 257.595 645.036 252.329C635.835 247.064 625.398 244.291 614.773 244.291C604.149 244.291 593.711 247.064 584.511 252.329C575.31 257.595 567.67 265.169 562.36 274.289L504.908 372.979L392.581 179.993C387.266 170.874 379.623 163.301 370.42 158.036C361.216 152.772 350.777 150 340.151 150C329.525 150 319.086 152.772 309.883 158.036C300.679 163.301 293.036 170.874 287.721 179.993L8.12649 660.002C2.80743 669.12 0.00462935 679.465 5.72978e-06 689.997C-0.00461789 700.529 2.78909 710.876 8.10015 719.999C13.4112 729.121 21.0523 736.696 30.255 741.963C39.4576 747.229 49.8973 750.001 60.524 750H270.538C353.748 750 415.112 713.775 457.336 643.101L559.849 467.145L614.757 372.979L779.547 655.834H559.849L504.908 750ZM267.114 655.737L120.551 655.704L340.249 278.586L449.87 467.145L376.474 593.175C348.433 639.03 316.577 655.737 267.114 655.737Z" fill="currentColor" />
</svg>
</template>

View File

@@ -0,0 +1,9 @@
<template>
<svg width="312" height="78" viewBox="0 0 312 78" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M65.6381 78H109.132C110.513 78.0002 111.87 77.6398 113.067 76.9552C114.263 76.2705 115.257 75.2857 115.947 74.0998C116.637 72.9139 117.001 71.5688 117 70.1996C116.999 68.8305 116.635 67.4856 115.944 66.3003L86.7343 16.1575C86.0439 14.9719 85.0508 13.9873 83.8547 13.3028C82.6586 12.6183 81.3017 12.2579 79.9205 12.2579C78.5393 12.2579 77.1825 12.6183 75.9864 13.3028C74.7903 13.9873 73.7971 14.9719 73.1067 16.1575L65.6381 28.9873L51.0356 3.89908C50.3446 2.71356 49.351 1.72914 48.1546 1.04472C46.9581 0.360308 45.6011 0 44.2196 0C42.8382 0 41.4811 0.360308 40.2847 1.04472C39.0883 1.72914 38.0947 2.71356 37.4037 3.89908L1.05644 66.3003C0.364965 67.4856 0.000601816 68.8305 7.44871e-07 70.1996C-0.000600326 71.5688 0.362582 72.9139 1.05302 74.0998C1.74346 75.2857 2.7368 76.2705 3.93315 76.9552C5.12949 77.6398 6.48665 78.0002 7.86812 78H35.1699C45.9872 78 53.9645 73.2907 59.4537 64.1032L72.7803 41.2289L79.9184 28.9873L101.341 65.7584H72.7803L65.6381 78ZM34.7248 65.7458L15.6717 65.7416L44.2324 16.7162L58.483 41.2289L48.9416 57.6127C45.2963 63.5739 41.155 65.7458 34.7248 65.7458Z" fill="currentColor" />
<path d="M175.417 77.3598V66.9562H149.03V21.3406H136.5V77.3598H175.417Z" fill="currentColor" />
<path d="M198.81 78C203.706 78 208.103 76.0793 210.178 73.1183V77.3598H221.795V37.026H210.178V41.0274C207.854 38.2264 203.706 36.3858 198.644 36.3858C186.446 36.3858 179.061 44.6286 179.061 57.1929C179.061 69.7572 186.446 78 198.81 78ZM200.635 68.3967C194.495 68.3967 190.429 63.9152 190.429 57.1929C190.429 50.3906 194.495 45.909 200.635 45.909C206.859 45.909 210.925 50.3906 210.925 57.1929C210.925 63.9152 206.859 68.3967 200.635 68.3967Z" fill="currentColor" />
<path d="M254.606 78C266.97 78 274.604 69.7572 274.604 57.1929C274.604 44.6286 266.97 36.3858 254.772 36.3858C249.544 36.3858 245.478 38.3064 243.155 41.2674V19.5H231.621V77.3598H243.155V72.9583C245.478 76.0793 249.793 78 254.606 78ZM252.78 68.3967C246.557 68.3967 242.491 63.9152 242.491 57.1929C242.491 50.3906 246.557 45.909 252.78 45.909C258.838 45.909 262.987 50.3906 262.987 57.1929C262.987 63.9152 258.838 68.3967 252.78 68.3967Z" fill="currentColor" />
<path d="M295.736 78C305.528 78 312 72.9583 312 65.2757C312 47.0294 289.098 56.3926 289.098 48.1498C289.098 45.5889 291.339 44.2285 294.575 44.2285C297.728 44.2285 300.964 46.0691 301.462 49.5103H311.502C311.087 41.5876 304.283 36.3858 294.243 36.3858C285.696 36.3858 279.307 41.4275 279.307 48.5499C279.307 65.5157 301.794 57.433 301.794 65.6758C301.794 67.9166 299.304 69.5971 295.736 69.5971C291.422 69.5971 288.517 67.3564 288.102 63.7551H278.145C278.56 72.4781 285.53 78 295.736 78Z" fill="currentColor" />
</svg>
</template>

View File

@@ -0,0 +1,128 @@
<template>
<ClientOnly>
<div class="inline-flex shadow-sm rounded-md">
<USelectMenu
v-model="primary"
name="primary"
class="!rounded-r-none !shadow-none focus:z-[1]"
color="gray"
:ui="{ width: 'w-[194px]' }"
:popper="{ placement: 'bottom-start' }"
:options="primaryOptions"
>
<template #label>
<span class="flex-shrink-0 h-3 w-3 rounded-full" :style="{ backgroundColor: `${primary.hex}`}" />
{{ primary.text }}
</template>
<template #option="{ option }">
<span class="flex-shrink-0 h-3 w-3 rounded-full" :style="{ backgroundColor: `${option.hex}`}" />
{{ option.text }}
</template>
</USelectMenu>
<USelectMenu
v-model="gray"
name="gray"
class="!rounded-l-none !shadow-none"
color="gray"
:ui="{ width: 'w-[194px]', wrapper: '-ml-px' }"
:popper="{ placement: 'bottom-end' }"
:options="grayOptions"
>
<template #label>
<span class="flex-shrink-0 h-3 w-3 rounded-full" :style="{ backgroundColor: `${gray.hex}`}" />
{{ gray.text }}
</template>
<template #option="{ option }">
<span class="flex-shrink-0 h-3 w-3 rounded-full" :style="{ backgroundColor: `${option.hex}`}" />
{{ option.text }}
</template>
</USelectMenu>
</div>
</ClientOnly>
</template>
<script setup lang="ts">
import colors from '#tailwind-config/theme/colors'
const appConfig = useAppConfig()
const colorMode = useColorMode()
const primaryCookie = useCookie('primary', { path: '/', default: () => appConfig.ui.primary })
const grayCookie = useCookie('gray', { path: '/', default: () => appConfig.ui.gray })
watch(primaryCookie, (primary) => {
appConfig.ui.primary = primary
}, { immediate: true })
watch(grayCookie, (gray) => {
appConfig.ui.gray = gray
}, { immediate: true })
// Computed
const primaryOptions = computed(() => useWithout(appConfig.ui.colors, 'primary').map(color => ({ value: color, text: color, hex: colors[color][colorMode.value === 'dark' ? 400 : 500] })))
const primary = computed({
get () {
return primaryOptions.value.find(option => option.value === primaryCookie.value) || primaryOptions.value.find(option => option.value === 'green')
},
set (option) {
primaryCookie.value = option.value
}
})
const grayOptions = computed(() => ['slate', 'cool', 'zinc', 'neutral', 'stone'].map(color => ({ value: color, text: color, hex: colors[color][colorMode.value === 'dark' ? 400 : 500] })))
const gray = computed({
get () {
return grayOptions.value.find(option => option.value === grayCookie.value) || grayOptions.value.find(option => option.value === 'cool')
},
set (option) {
grayCookie.value = option.value
}
})
// Hack for SSG
const hexToRgb = (hex) => {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
hex = hex.replace(shorthandRegex, function (_, r, g, b) {
return r + r + g + g + b + b
})
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result
? `${parseInt(result[1], 16)} ${parseInt(result[2], 16)} ${parseInt(result[3], 16)}`
: null
}
const root = computed(() => {
return `:root {
${Object.entries(colors[primary.value.value] || colors.green).map(([key, value]) => `--color-primary-${key}: ${hexToRgb(value)};`).join('\n')}
${Object.entries(colors[gray.value.value] || colors.cool).map(([key, value]) => `--color-gray-${key}: ${hexToRgb(value)};`).join('\n')}
}`
})
if (process.client) {
watch(root, () => {
window.localStorage.setItem('nuxt-ui-root', root.value)
}, { immediate: true })
}
if (process.server) {
useHead({
script: [
{
innerHTML: `
if (localStorage.getItem('nuxt-ui-root')) {
document.querySelector('style#nuxt-ui-colors').innerHTML = localStorage.getItem('nuxt-ui-root')
}`.replace(/\s+/g, ' '),
type: 'text/javascript',
tagPriority: -1
}
]
})
}
</script>

View File

@@ -0,0 +1,33 @@
<template>
<component
:is="to ? NuxtLink : 'div'"
:to="to"
class="block pl-4 pr-6 py-3 rounded-md !border !border-gray-200 dark:!border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-sm leading-6 my-5 last:mb-0 font-normal group relative prose-code:bg-white dark:prose-code:bg-gray-900"
:class="[to ? 'hover:!border-primary-500 dark:hover:!border-primary-400 hover:text-primary-500 dark:hover:text-primary-400 border-dashed hover:text-gray-800 dark:hover:text-gray-200' : '']"
>
<UIcon v-if="!!to" name="i-heroicons-link-20-solid" class="w-3 h-3 absolute right-2 top-2 text-gray-400 dark:text-gray-500 group-hover:text-primary-500 dark:group-hover:text-primary-400" />
<UIcon v-if="icon" :name="icon" class="w-4 h-4 mr-2 inline-flex items-center align-text-top" :class="color" />
<ContentSlot :use="$slots.default" unwrap="p" />
</component>
</template>
<script setup lang="ts">
const NuxtLink = resolveComponent('NuxtLink')
defineProps({
icon: {
type: String,
default: null
},
color: {
type: String,
default: 'text-primary-500 dark:text-primary-400'
},
to: {
type: String,
default: null
}
})
</script>

View File

@@ -0,0 +1,18 @@
<template>
<UKbd class="!my-0 align-text-top">
{{ shortcut }}
</UKbd>
</template>
<script setup lang="ts">
const props = defineProps({
value: {
type: String,
required: true
}
})
const { metaSymbol } = useShortcuts()
const shortcut = computed(() => props.value === 'meta' ? metaSymbol.value : props.value)
</script>

View File

@@ -0,0 +1,53 @@
<template>
<div :selected-index="selectedIndex" @change="changeTab">
<div class="flex border border-gray-200 dark:border-gray-700 border-b-0 rounded-t-md overflow-hidden -mb-px">
<div
v-for="(tab, index) in tabs"
:key="index"
as="template"
@click="selectedIndex = index"
>
<button
class="px-4 py-2 focus:outline-none text-sm border-r border-r-gray-200 dark:border-r-gray-700 transition-colors"
tabindex="-1"
:class="[selectedIndex === index ? 'font-medium text-primary-500 dark:text-primary-400 bg-gray-50 dark:bg-gray-800' : 'hover:bg-gray-50 dark:hover:bg-gray-800']"
>
{{ tab.label }}
</button>
</div>
</div>
<div class="[&>div>pre]:!rounded-t-none">
<component :is="selectedTab.component" />
</div>
</div>
</template>
<script setup lang="ts">
const slots = useSlots()
const selectedIndex = ref(0)
// Computed
const tabs = computed(() => slots.default?.().map((slot, index) => {
return {
label: slot.props?.filename || slot.props?.label || `${index}`,
component: slot
}
}) || [])
const selectedTab = computed(() => tabs.value.find((_, index) => index === selectedIndex.value))
// Methods
function changeTab (index) {
selectedIndex.value = index
}
</script>
<script lang="ts">
export default {
inheritAttrs: false
}
</script>

View File

@@ -0,0 +1,213 @@
<template>
<div>
<div v-if="propsToSelect.length" class="relative flex border border-gray-200 dark:border-gray-700 rounded-t-md overflow-hidden not-prose">
<div v-for="prop in propsToSelect" :key="prop.name" class="flex flex-col gap-0.5 justify-between py-1.5 font-medium bg-gray-50 dark:bg-gray-800 border-r border-r-gray-200 dark:border-r-gray-700">
<label :for="`prop-${prop.name}`" class="block text-xs px-3 font-medium text-gray-400 dark:text-gray-500 -my-px">{{ prop.label }}</label>
<UCheckbox
v-if="prop.type === 'boolean'"
v-model="componentProps[prop.name]"
:name="`prop-${prop.name}`"
variant="none"
:ui="{ wrapper: 'relative flex items-start justify-center' }"
/>
<USelectMenu
v-else-if="prop.type === 'string' && prop.options.length"
v-model="componentProps[prop.name]"
:options="prop.options"
:name="`prop-${prop.name}`"
variant="none"
:ui="{ width: 'w-32 !-mt-px', rounded: 'rounded-b-md', wrapper: 'relative inline-flex' }"
class="!py-0"
:popper="{ strategy: 'fixed', placement: 'bottom-start' }"
/>
<UInput
v-else
:model-value="componentProps[prop.name]"
:type="prop.type === 'number' ? 'number' : 'text'"
:name="`prop-${prop.name}`"
variant="none"
autocomplete="off"
class="!py-0"
@update:model-value="val => componentProps[prop.name] = prop.type === 'number' ? Number(val) : val"
/>
</div>
</div>
<div class="flex border border-b-0 border-gray-200 dark:border-gray-700 relative not-prose" :class="[{ 'p-4': padding }, propsToSelect.length ? 'border-t-0' : 'rounded-t-md', backgroundClass, overflowClass]">
<component :is="name" v-model="vModel" v-bind="fullProps">
<ContentSlot v-if="$slots.default" :use="$slots.default" />
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
<ClientOnly>
<ContentSlot v-if="$slots[slot]" :use="$slots[slot]" />
</ClientOnly>
</template>
</component>
</div>
<ContentRenderer :value="ast" class="[&>div>pre]:!rounded-t-none" />
</div>
</template>
<script setup lang="ts">
// @ts-expect-error
import { transformContent } from '@nuxt/content/transformers'
// eslint-disable-next-line vue/no-dupe-keys
const props = defineProps({
slug: {
type: String,
default: null
},
padding: {
type: Boolean,
default: true
},
props: {
type: Object,
default: () => ({})
},
code: {
type: String,
default: null
},
slots: {
type: Object,
default: null
},
baseProps: {
type: Object,
default: () => ({})
},
ui: {
type: Object,
default: () => ({})
},
excludedProps: {
type: Array,
default: () => []
},
extraColors: {
type: Array,
default: () => []
},
backgroundClass: {
type: String,
default: 'bg-white dark:bg-gray-900'
},
overflowClass: {
type: String,
default: ''
}
})
// eslint-disable-next-line vue/no-dupe-keys
const baseProps = reactive({ ...props.baseProps })
const componentProps = reactive({ ...props.props })
const appConfig = useAppConfig()
const route = useRoute()
// eslint-disable-next-line vue/no-dupe-keys
const slug = props.slug || route.params.slug[1]
const camelName = useCamelCase(slug)
const name = `U${useUpperFirst(camelName)}`
const meta = await fetchComponentMeta(name)
// Computed
// eslint-disable-next-line vue/no-dupe-keys
const ui = computed(() => ({ ...appConfig.ui[camelName], ...props.ui }))
const fullProps = computed(() => ({ ...baseProps, ...componentProps }))
const vModel = computed({
get: () => baseProps.modelValue,
set: (value) => {
baseProps.modelValue = value
}
})
const propsToSelect = computed(() => Object.keys(componentProps).map((key) => {
if (props.excludedProps.includes(key)) {
return null
}
const prop = meta?.meta?.props?.find((prop: any) => prop.name === key)
const dottedKey = useKebabCase(key).replaceAll('-', '.')
const keys = useGet(ui.value, dottedKey, {})
let options = typeof keys === 'object' && Object.keys(keys)
if (key.toLowerCase().endsWith('color')) {
// @ts-ignore
options = [...appConfig.ui.colors, ...props.extraColors]
}
return {
type: prop?.type || 'string',
name: key,
label: key === 'modelValue' ? 'value' : useCamelCase(key),
options
}
}).filter(Boolean))
// eslint-disable-next-line vue/no-dupe-keys
const code = computed(() => {
let code = `\`\`\`html
<${name}`
for (const [key, value] of Object.entries(componentProps)) {
if (value === 'undefined' || value === null) {
continue
}
const prop = meta?.meta?.props?.find((prop: any) => prop.name === key)
code += ` ${(prop?.type === 'boolean' && value !== true) || typeof value === 'object' ? ':' : ''}${key === 'modelValue' ? 'value' : useKebabCase(key)}${prop?.type === 'boolean' && !!value && key !== 'modelValue' ? '' : `="${typeof value === 'object' ? renderObject(value) : value}"`}`
}
if (props.slots) {
code += `>
${Object.entries(props.slots).map(([key, value]) => `<template #${key}>
${value}
</template>`).join('\n ')}
</${name}>`
} else if (props.code) {
const lineBreaks = (props.code.match(/\n/g) || []).length
if (lineBreaks > 1) {
code += `>
${props.code}</${name}>`
} else {
code += `>${props.code}</${name}>`
}
} else {
code += ' />'
}
code += `
\`\`\`
`
return code
})
function renderObject (obj: any) {
if (Array.isArray(obj)) {
return `[${obj.map(renderObject).join(', ')}]`
}
if (typeof obj === 'object') {
return `{ ${Object.entries(obj).map(([key, value]) => `${key}: ${renderObject(value)}`).join(', ')} }`
}
if (typeof obj === 'string') {
return `'${obj}'`
}
return obj
}
const { data: ast } = await useAsyncData(`${name}-ast-${JSON.stringify(props)}`, () => transformContent('content:_markdown.md', code.value, {
highlight: {
theme: {
light: 'material-lighter',
dark: 'material-palenight'
}
}
}), { watch: [code] })
</script>

View File

@@ -0,0 +1,26 @@
<template>
<div class="[&>div>pre]:!rounded-t-none">
<div class="flex border border-gray-200 dark:border-gray-700 relative not-prose rounded-t-md" :class="[{ 'p-4': padding, 'rounded-b-md': !$slots.code, 'border-b-0': !!$slots.code }, backgroundClass, overflowClass]">
<ContentSlot v-if="$slots.default" :use="$slots.default" />
</div>
<ContentSlot v-if="$slots.code" :use="$slots.code" />
</div>
</template>
<script setup lang="ts">
defineProps({
padding: {
type: Boolean,
default: true
},
backgroundClass: {
type: String,
default: 'bg-white dark:bg-gray-900'
},
overflowClass: {
type: String,
default: ''
}
})
</script>

View File

@@ -0,0 +1,37 @@
<template>
<ContentRenderer :value="ast" />
</template>
<script setup lang="ts">
// @ts-expect-error
import { transformContent } from '@nuxt/content/transformers'
const props = defineProps({
slug: {
type: String,
default: null
}
})
const appConfig = useAppConfig()
const route = useRoute()
// eslint-disable-next-line vue/no-dupe-keys
const slug = props.slug || route.params.slug[1]
const camelName = useCamelCase(slug)
const name = `U${useUpperFirst(camelName)}`
const preset = appConfig.ui[camelName]
const { data: ast } = await useAsyncData(`${name}-preset`, () => transformContent('content:_markdown.md', `
\`\`\`json
${JSON.stringify(preset, null, 2)}
\`\`\`\
`, {
highlight: {
theme: {
light: 'material-lighter',
dark: 'material-palenight'
}
}
}))
</script>

View File

@@ -0,0 +1,56 @@
<template>
<div>
<table class="table-fixed">
<thead>
<tr>
<th class="w-[25%]">
Prop
</th>
<th class="w-[50%]">
Default
</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="prop in metaProps" :key="prop.name">
<td class="relative flex-shrink-0">
<code>{{ prop.name }}</code><span v-if="prop.required" class="font-bold text-red-500 dark:text-red-400 absolute top-0 ml-1">*</span>
</td>
<td>
<code v-if="prop.default">{{ prop.default }}</code>
</td>
<td>
<a v-if="prop.name === 'ui'" href="#preset">
<code>{{ prop.type }}</code>
</a>
<code v-else class="break-all">
{{ prop.type }}
</code>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
slug: {
type: String,
default: null
}
})
const route = useRoute()
// eslint-disable-next-line vue/no-dupe-keys
const slug = props.slug || route.params.slug[1]
const camelName = useCamelCase(slug)
const name = `U${useUpperFirst(camelName)}`
const meta = await fetchComponentMeta(name)
const metaProps = computed(() => useSortBy(meta?.meta?.props || [], [
prop => ['string', 'number', 'boolean', 'any'].indexOf(prop.type)
]))
</script>

View File

@@ -0,0 +1,35 @@
<template>
<div>
<table>
<thead>
<tr>
<th>Slot</th>
</tr>
</thead>
<tbody>
<tr v-for="slot in (meta.meta.slots as any[])" :key="slot.name">
<td class="whitespace-nowrap">
<code>{{ slot.name }}</code>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
slug: {
type: String,
default: null
}
})
const route = useRoute()
// eslint-disable-next-line vue/no-dupe-keys
const slug = props.slug || route.params.slug[1]
const camelName = useCamelCase(slug)
const name = `U${useUpperFirst(camelName)}`
const meta = await fetchComponentMeta(name)
</script>

View File

@@ -0,0 +1,21 @@
<template>
<div class="relative overflow-hidden rounded border border-dashed border-gray-400 dark:border-gray-500 opacity-75">
<svg class="absolute inset-0 h-full w-full stroke-gray-900/10 dark:stroke-white/10" fill="none">
<defs>
<pattern
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"
x="0"
y="0"
width="10"
height="10"
patternUnits="userSpaceOnUse"
>
<path d="M-3 13 15-5M-5 5l18-18M-1 21 17 3" />
</pattern>
</defs>
<rect stroke="none" fill="url(#pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e)" width="100%" height="100%" />
</svg>
<slot />
</div>
</template>

View File

@@ -0,0 +1,17 @@
<template>
<iframe :src="src" class="w-full min-h-[calc(100vh/1.5)] border border-gray-200 dark:border-gray-800 rounded-md" />
</template>
<script setup lang="ts">
const props = defineProps({
token: {
type: String,
required: true
}
})
const appConfig = useAppConfig()
const colorMode = useColorMode()
const src = computed(() => `https://volta.net/embed/${props.token}?theme=${colorMode.value}&gray=${appConfig.ui.gray}&primary=${appConfig.ui.primary}`)
</script>

View File

@@ -0,0 +1,13 @@
<template>
<UCard>
<template #header>
<Placeholder class="h-8" />
</template>
<Placeholder class="h-32" />
<template #footer>
<Placeholder class="h-8" />
</template>
</UCard>
</template>

View File

@@ -0,0 +1,7 @@
<script setup>
const selected = ref(false)
</script>
<template>
<UCheckbox v-model="selected" name="notifications" label="Notifications" />
</template>

View File

@@ -0,0 +1,21 @@
<script setup>
const groups = computed(() => {
return [{
key: 'users',
label: q => q && `Users matching “${q}”...`,
search: async (q) => {
if (!q) {
return []
}
const users = await $fetch(`https://jsonplaceholder.typicode.com/users`, { params: { q } })
return users.map(user => ({ id: user.id, label: user.name, suffix: user.email }))
}
}].filter(Boolean)
})
</script>
<template>
<UCommandPalette :groups="groups" :autoselect="false" />
</template>

View File

@@ -0,0 +1,27 @@
<script setup>
const people = [
{ id: 1, label: 'Wade Cooper' },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb' },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox' },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz' },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham' },
{ id: 10, label: 'Emil Schaefer' }
]
const selected = ref([people[3]])
</script>
<template>
<UCommandPalette
v-model="selected"
multiple
nullable
:autoselect="false"
:groups="[{ key: 'people', commands: people }]"
:fuse="{ resultLimit: 6, fuseOptions: { threshold: 0.1 } }"
/>
</template>

View File

@@ -0,0 +1,10 @@
<template>
<UCommandPalette>
<template #empty-state>
<div class="flex flex-col items-center justify-center py-6 gap-3">
<span class="italic text-sm">Nothing here!</span>
<UButton label="Add item" />
</div>
</template>
</UCommandPalette>
</template>

View File

@@ -0,0 +1,46 @@
<script setup>
const router = useRouter()
const commandPaletteRef = ref()
const users = [
{ id: 'benjamincanac', label: 'benjamincanac', href: 'https://github.com/benjamincanac', target: '_blank', avatar: { src: 'https://avatars.githubusercontent.com/u/739984?v=4' } },
{ id: 'Atinux', label: 'Atinux', href: 'https://github.com/Atinux', target: '_blank', avatar: { src: 'https://avatars.githubusercontent.com/u/904724?v=4' } },
{ id: 'smarroufin', label: 'smarroufin', href: 'https://github.com/smarroufin', target: '_blank', avatar: { src: 'https://avatars.githubusercontent.com/u/7547335?v=4' } }
]
const actions = [
{ id: 'new-file', label: 'Add new file', icon: 'i-heroicons-document-plus', click: () => alert('New file'), shortcuts: ['⌘', 'N'] },
{ id: 'new-folder', label: 'Add new folder', icon: 'i-heroicons-folder-plus', click: () => alert('New folder'), shortcuts: ['⌘', 'F'] },
{ id: 'hashtag', label: 'Add hashtag', icon: 'i-heroicons-hashtag', click: () => alert('Add hashtag'), shortcuts: ['⌘', 'H'] },
{ id: 'label', label: 'Add label', icon: 'i-heroicons-tag', click: () => alert('Add label'), shortcuts: ['⌘', 'L'] }
]
const groups = computed(() => commandPaletteRef.value?.query
? [{
key: 'users',
commands: users
}]
: [{
key: 'recent',
label: 'Recent searches',
commands: users.slice(0, 1)
}, {
key: 'actions',
commands: actions
}])
function onSelect (option) {
if (option.click) {
option.click()
} else if (option.to) {
router.push(option.to)
} else if (option.href) {
window.open(option.href, '_blank')
}
}
</script>
<template>
<UCommandPalette ref="commandPaletteRef" :groups="groups" :autoselect="false" @update:model-value="onSelect" />
</template>

View File

@@ -0,0 +1,33 @@
<script setup>
const isOpen = ref(false)
const people = [
{ id: 1, label: 'Wade Cooper' },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb' },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox' },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz' },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham' },
{ id: 10, label: 'Emil Schaefer' }
]
const selected = ref([])
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen">
<UCommandPalette
v-model="selected"
multiple
nullable
:groups="[{ key: 'people', commands: people }]"
/>
</UModal>
</div>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<UContainer>
<Placeholder class="h-32" />
</UContainer>
</template>

View File

@@ -0,0 +1,35 @@
<script setup>
const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()
const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
function onContextMenu () {
const top = unref(y) - unref(windowY)
const left = unref(x)
virtualElement.value.getBoundingClientRect = () => ({
width: 0,
height: 0,
top,
left
})
isOpen.value = true
}
</script>
<template>
<div class="w-full" @contextmenu.prevent="onContextMenu">
<Placeholder class="h-96 select-none w-full flex items-center justify-center">
Right click here
</Placeholder>
<UContextMenu v-model="isOpen" :virtual-element="virtualElement">
<div class="p-4">
Menu
</div>
</UContextMenu>
</div>
</template>

View File

@@ -0,0 +1,38 @@
<script setup>
const items = [
[{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}], [{
label: 'Edit',
icon: 'i-heroicons-pencil-square-20-solid',
shortcuts: ['E'],
click: () => {
console.log('Edit')
}
}, {
label: 'Duplicate',
icon: 'i-heroicons-document-duplicate-20-solid',
shortcuts: ['D'],
disabled: true
}], [{
label: 'Archive',
icon: 'i-heroicons-archive-box-20-solid'
}, {
label: 'Move',
icon: 'i-heroicons-arrow-right-circle-20-solid'
}], [{
label: 'Delete',
icon: 'i-heroicons-trash-20-solid',
shortcuts: ['⌘', 'D']
}]
]
</script>
<template>
<UDropdown :items="items" :popper="{ placement: 'bottom-start' }">
<UButton color="white" label="Options" trailing-icon="i-heroicons-chevron-down-20-solid" />
</UDropdown>
</template>

View File

@@ -0,0 +1,16 @@
<script setup>
const items = [
[{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}]
]
</script>
<template>
<UDropdown :items="items" mode="hover" :popper="{ placement: 'bottom-start' }">
<UButton color="white" label="Options" trailing-icon="i-heroicons-chevron-down-20-solid" />
</UDropdown>
</template>

View File

@@ -0,0 +1,7 @@
<script setup>
const value = ref('')
</script>
<template>
<UInput v-model="value" />
</template>

View File

@@ -0,0 +1,18 @@
<template>
<UInput v-model="q" name="q" placeholder="Search..." icon="i-heroicons-magnifying-glass-20-solid" :ui="{ icon: { trailing: { pointer: '' } } }">
<template #trailing>
<UButton
v-show="q !== ''"
color="gray"
variant="link"
icon="i-heroicons-x-mark-20-solid"
:padded="false"
@click="q = ''"
/>
</template>
</UInput>
</template>
<script setup lang="ts">
const q = ref('')
</script>

View File

@@ -0,0 +1,10 @@
<script setup>
const { metaSymbol } = useShortcuts()
</script>
<template>
<div class="flex items-center gap-0.5">
<UKbd>{{ metaSymbol }}</UKbd>
<UKbd>K</UKbd>
</div>
</template>

View File

@@ -0,0 +1,15 @@
<script setup>
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen">
<div class="p-4">
<Placeholder class="h-48" />
</div>
</UModal>
</div>
</template>

View File

@@ -0,0 +1,23 @@
<script setup>
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen">
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<template #header>
<Placeholder class="h-8" />
</template>
<Placeholder class="h-32" />
<template #footer>
<Placeholder class="h-8" />
</template>
</UCard>
</UModal>
</div>
</template>

View File

@@ -0,0 +1,15 @@
<script setup>
const toast = useToast()
const actions = ref([{
label: 'Action 1',
click: () => alert('Action 1 clicked!')
}, {
label: 'Action 2',
click: () => alert('Action 2 clicked!')
}])
</script>
<template>
<UButton label="Show toast" @click="toast.add({ title: 'With actions', actions })" />
</template>

View File

@@ -0,0 +1,7 @@
<script setup>
const toast = useToast()
</script>
<template>
<UButton label="Show toast" @click="toast.add({ title: 'Hello world!' })" />
</template>

View File

@@ -0,0 +1,11 @@
<script setup>
const toast = useToast()
function onCallback () {
alert('Notification expired!')
}
</script>
<template>
<UButton label="Show toast" @click="toast.add({ title: 'Expires soon...', timeout: 1000, callback: onCallback })" />
</template>

View File

@@ -0,0 +1,11 @@
<script setup>
const toast = useToast()
function onClick () {
alert('Clicked!')
}
</script>
<template>
<UButton label="Show toast" @click="toast.add({ title: 'Click me', click: onClick })" />
</template>

View File

@@ -0,0 +1,8 @@
<script setup>
const page = ref(1)
const items = ref(Array(55))
</script>
<template>
<UPagination v-model="page" :page-count="5" :total="items.length" />
</template>

View File

@@ -0,0 +1,20 @@
<script setup>
const page = ref(1)
const items = ref(Array(55))
</script>
<template>
<UPagination v-model="page" :total="items.length" :ui="{ rounded: 'first-of-type:rounded-l-md last-of-type:rounded-r-md' }">
<template #prev="{ onClick }">
<UTooltip text="Previous page">
<UButton icon="i-heroicons-arrow-small-left-20-solid" color="primary" :ui="{ rounded: 'rounded-full' }" class="mr-2" @click="onClick" />
</UTooltip>
</template>
<template #next="{ onClick }">
<UTooltip text="Next page">
<UButton icon="i-heroicons-arrow-small-right-20-solid" color="primary" :ui="{ rounded: 'rounded-full' }" class="ml-2" @click="onClick" />
</UTooltip>
</template>
</UPagination>
</template>

View File

@@ -0,0 +1,11 @@
<template>
<UPopover>
<UButton color="white" label="Open" trailing-icon="i-heroicons-chevron-down-20-solid" />
<template #panel>
<div class="p-4">
<Placeholder class="h-20 w-48" />
</div>
</template>
</UPopover>
</template>

View File

@@ -0,0 +1,23 @@
<script setup>
const methods = [{
name: 'email',
value: 'email',
label: 'Email'
}, {
name: 'sms',
value: 'sms',
label: 'Phone (SMS)'
}, {
name: 'push',
value: 'push',
label: 'Push notification'
}]
const selected = ref('sms')
</script>
<template>
<div class="space-y-1">
<URadio v-for="method of methods" :key="method.name" v-model="selected" v-bind="method" />
</div>
</template>

View File

@@ -0,0 +1,9 @@
<script setup>
const countries = ['United States', 'Canada', 'Mexico']
const country = ref(countries[0])
</script>
<template>
<USelect v-model="country" :options="countries" />
</template>

View File

@@ -0,0 +1,18 @@
<script setup>
const countries = [{
name: 'United States',
value: 'US'
}, {
name: 'Canada',
value: 'CA'
}, {
name: 'Mexico',
value: 'MX'
}]
const country = ref('CA')
</script>
<template>
<USelect v-model="country" :options="countries" option-attribute="name" />
</template>

View File

@@ -0,0 +1,9 @@
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref(people[0])
</script>
<template>
<USelectMenu v-model="selected" :options="people" />
</template>

View File

@@ -0,0 +1,15 @@
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref(people[3])
</script>
<template>
<USelectMenu v-slot="{ open }" v-model="selected" :options="people">
<UButton color="gray">
{{ selected }}
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform text-gray-400 dark:text-gray-500" :class="[open && 'transform rotate-90']" />
</UButton>
</USelectMenu>
</template>

View File

@@ -0,0 +1,9 @@
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref([])
</script>
<template>
<USelectMenu v-model="selected" :options="people" multiple placeholder="Select people" />
</template>

View File

@@ -0,0 +1,14 @@
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref([])
</script>
<template>
<USelectMenu v-model="selected" :options="people" multiple>
<template #label>
<span v-if="selected.length" class="truncate">{{ selected.join(', ') }}</span>
<span v-else>Select people</span>
</template>
</USelectMenu>
</template>

View File

@@ -0,0 +1,41 @@
<script setup>
const people = [{
id: 'benjamincanac',
label: 'benjamincanac',
href: 'https://github.com/benjamincanac',
target: '_blank',
avatar: { src: 'https://avatars.githubusercontent.com/u/739984?v=4' }
},
{
id: 'Atinux',
label: 'Atinux',
href: 'https://github.com/Atinux',
target: '_blank',
avatar: { src: 'https://avatars.githubusercontent.com/u/904724?v=4' }
},
{
id: 'smarroufin',
label: 'smarroufin',
href: 'https://github.com/smarroufin',
target: '_blank',
avatar: { src: 'https://avatars.githubusercontent.com/u/7547335?v=4' }
},
{
id: 'nobody',
label: 'Nobody',
icon: 'i-heroicons-user-circle'
}]
const selected = ref(people[0])
</script>
<template>
<USelectMenu v-model="selected" :options="people">
<template #label>
<UIcon v-if="selected.icon" :name="selected.icon" class="w-4 h-4" />
<UAvatar v-else-if="selected.avatar" v-bind="selected.avatar" size="3xs" />
{{ selected.label }}
</template>
</USelectMenu>
</template>

View File

@@ -0,0 +1,9 @@
<template>
<div class="flex items-center space-x-4">
<USkeleton class="h-12 w-12" :ui="{ rounded: 'rounded-full' }" />
<div class="space-y-2">
<USkeleton class="h-4 w-[250px]" />
<USkeleton class="h-4 w-[200px]" />
</div>
</div>
</template>

View File

@@ -0,0 +1,30 @@
<script setup>
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<USlideover v-model="isOpen">
<div class="p-4 sm:p-6 flex flex-col flex-1 gap-4 sm:gap-6">
<div class="flex items-center justify-between">
<h2 class="font-semibold text-gray-900 dark:text-white">
Title
</h2>
<UButton
icon="i-heroicons-x-mark-20-solid"
color="gray"
variant="link"
size="md"
:padded="false"
@click="isOpen = false"
/>
</div>
<Placeholder class="flex-1 w-full" />
</div>
</USlideover>
</div>
</template>

View File

@@ -0,0 +1,43 @@
<script setup>
const people = [{
id: 1,
name: 'Lindsay Walton',
title: 'Front-end Developer',
email: 'lindsay.walton@example.com',
role: 'Member'
}, {
id: 2,
name: 'Courtney Henry',
title: 'Designer',
email: 'courtney.henry@example.com',
role: 'Admin'
}, {
id: 3,
name: 'Tom Cook',
title: 'Director of Product',
email: 'tom.cook@example.com',
role: 'Member'
}, {
id: 4,
name: 'Whitney Francis',
title: 'Copywriter',
email: 'whitney.francis@example.com',
role: 'Admin'
}, {
id: 5,
name: 'Leonard Krasner',
title: 'Senior Designer',
email: 'leonard.krasner@example.com',
role: 'Owner'
}, {
id: 6,
name: 'Floyd Miles',
title: 'Principal Designer',
email: 'floyd.miles@example.com',
role: 'Member'
}]
</script>
<template>
<UTable :rows="people" />
</template>

View File

@@ -0,0 +1,59 @@
<script setup>
const columns = [{
key: 'id',
label: 'ID'
}, {
key: 'name',
label: 'User name'
}, {
key: 'title',
label: 'Job position'
}, {
key: 'email',
label: 'Email'
}, {
key: 'role'
}]
const people = [{
id: 1,
name: 'Lindsay Walton',
title: 'Front-end Developer',
email: 'lindsay.walton@example.com',
role: 'Member'
}, {
id: 2,
name: 'Courtney Henry',
title: 'Designer',
email: 'courtney.henry@example.com',
role: 'Admin'
}, {
id: 3,
name: 'Tom Cook',
title: 'Director of Product',
email: 'tom.cook@example.com',
role: 'Member'
}, {
id: 4,
name: 'Whitney Francis',
title: 'Copywriter',
email: 'whitney.francis@example.com',
role: 'Admin'
}, {
id: 5,
name: 'Leonard Krasner',
title: 'Senior Designer',
email: 'leonard.krasner@example.com',
role: 'Owner'
}, {
id: 6,
name: 'Floyd Miles',
title: 'Principal Designer',
email: 'floyd.miles@example.com',
role: 'Member'
}]
</script>
<template>
<UTable :columns="columns" :rows="people" />
</template>

View File

@@ -0,0 +1,68 @@
<script setup>
const columns = [{
key: 'id',
label: 'ID'
}, {
key: 'name',
label: 'Name'
}, {
key: 'title',
label: 'Title'
}, {
key: 'email',
label: 'Email'
}, {
key: 'role',
label: 'Role'
}]
const selectedColumns = ref([...columns])
const people = [{
id: 1,
name: 'Lindsay Walton',
title: 'Front-end Developer',
email: 'lindsay.walton@example.com',
role: 'Member'
}, {
id: 2,
name: 'Courtney Henry',
title: 'Designer',
email: 'courtney.henry@example.com',
role: 'Admin'
}, {
id: 3,
name: 'Tom Cook',
title: 'Director of Product',
email: 'tom.cook@example.com',
role: 'Member'
}, {
id: 4,
name: 'Whitney Francis',
title: 'Copywriter',
email: 'whitney.francis@example.com',
role: 'Admin'
}, {
id: 5,
name: 'Leonard Krasner',
title: 'Senior Designer',
email: 'leonard.krasner@example.com',
role: 'Owner'
}, {
id: 6,
name: 'Floyd Miles',
title: 'Principal Designer',
email: 'floyd.miles@example.com',
role: 'Member'
}]
</script>
<template>
<div>
<div class="flex px-3 py-3.5 border-b border-gray-200 dark:border-gray-700">
<USelectMenu v-model="selectedColumns" :options="columns" multiple placeholder="Columns" />
</div>
<UTable :columns="selectedColumns" :rows="people" />
</div>
</template>

View File

@@ -0,0 +1,64 @@
<script setup>
const columns = [{
key: 'id',
label: 'ID'
}, {
key: 'name',
label: 'Name',
sortable: true
}, {
key: 'title',
label: 'Title',
sortable: true
}, {
key: 'email',
label: 'Email',
sortable: true,
direction: 'desc'
}, {
key: 'role',
label: 'Role'
}]
const people = [{
id: 1,
name: 'Lindsay Walton',
title: 'Front-end Developer',
email: 'lindsay.walton@example.com',
role: 'Member'
}, {
id: 2,
name: 'Courtney Henry',
title: 'Designer',
email: 'courtney.henry@example.com',
role: 'Admin'
}, {
id: 3,
name: 'Tom Cook',
title: 'Director of Product',
email: 'tom.cook@example.com',
role: 'Member'
}, {
id: 4,
name: 'Whitney Francis',
title: 'Copywriter',
email: 'whitney.francis@example.com',
role: 'Admin'
}, {
id: 5,
name: 'Leonard Krasner',
title: 'Senior Designer',
email: 'leonard.krasner@example.com',
role: 'Owner'
}, {
id: 6,
name: 'Floyd Miles',
title: 'Principal Designer',
email: 'floyd.miles@example.com',
role: 'Member'
}]
</script>
<template>
<UTable :columns="columns" :rows="people" :sort="{ column: 'title' }" />
</template>

View File

@@ -0,0 +1,30 @@
<script setup>
const columns = [{
key: 'name',
label: 'Name'
}, {
key: 'title',
label: 'Title'
}, {
key: 'email',
label: 'Email'
}, {
key: 'role',
label: 'Role'
}, {
key: 'actions'
}]
const people = []
</script>
<template>
<UTable :rows="people" :columns="columns">
<template #empty-state>
<div class="flex flex-col items-center justify-center py-6 gap-3">
<span class="italic text-sm">No one here!</span>
<UButton label="Add people" />
</div>
</template>
</UTable>
</template>

View File

@@ -0,0 +1,86 @@
<script setup>
const columns = [{
key: 'name',
label: 'Name'
}, {
key: 'title',
label: 'Title'
}, {
key: 'email',
label: 'Email'
}, {
key: 'role',
label: 'Role'
}, {
key: 'actions'
}]
const people = []
const pending = ref(true)
</script>
<template>
<UTable :rows="people" :columns="columns" :loading="pending">
<template #loading-state>
<div class="flex items-center justify-center h-32">
<i class="loader --6" />
</div>
</template>
</UTable>
</template>
<style scoped>
/* https://codepen.io/jenning/pen/YzNmzaV */
.loader {
--color: rgb(var(--color-primary-400));
--size-mid: 6vmin;
--size-dot: 1.5vmin;
--size-bar: 0.4vmin;
--size-square: 3vmin;
display: block;
position: relative;
width: 50%;
display: grid;
place-items: center;
}
.loader::before,
.loader::after {
content: '';
box-sizing: border-box;
position: absolute;
}
/**
loader --6
**/
.loader.--6::before {
width: var(--size-square);
height: var(--size-square);
background-color: var(--color);
top: calc(50% - var(--size-square));
left: calc(50% - var(--size-square));
animation: loader-6 2.4s cubic-bezier(0, 0, 0.24, 1.21) infinite;
}
@keyframes loader-6 {
0%, 100% {
transform: none;
}
25% {
transform: translateX(100%);
}
50% {
transform: translateX(100%) translateY(100%);
}
75% {
transform: translateY(100%);
}
}
</style>

View File

@@ -0,0 +1,98 @@
<script setup>
const people = [{
id: 1,
name: 'Lindsay Walton',
title: 'Front-end Developer',
email: 'lindsay.walton@example.com',
role: 'Member'
}, {
id: 2,
name: 'Courtney Henry',
title: 'Designer',
email: 'courtney.henry@example.com',
role: 'Admin'
}, {
id: 3,
name: 'Tom Cook',
title: 'Director of Product',
email: 'tom.cook@example.com',
role: 'Member'
}, {
id: 4,
name: 'Whitney Francis',
title: 'Copywriter',
email: 'whitney.francis@example.com',
role: 'Admin'
}, {
id: 5,
name: 'Leonard Krasner',
title: 'Senior Designer',
email: 'leonard.krasner@example.com',
role: 'Owner'
}, {
id: 6,
name: 'Floyd Miles',
title: 'Principal Designer',
email: 'floyd.miles@example.com',
role: 'Member'
}, {
id: 7,
name: 'Emily Selman',
title: 'VP, User Experience',
email: '',
role: 'Admin'
}, {
id: 8,
name: 'Kristin Watson',
title: 'VP, Human Resources',
email: '',
role: 'Member'
}, {
id: 9,
name: 'Emma Watson',
title: 'Front-end Developer',
email: '',
role: 'Member'
}, {
id: 10,
name: 'John Doe',
title: 'Designer',
email: '',
role: 'Admin'
}, {
id: 11,
name: 'Jane Doe',
title: 'Director of Product',
email: '',
role: 'Member'
}, {
id: 12,
name: 'John Smith',
title: 'Copywriter',
email: '',
role: 'Admin'
}, {
id: 13,
name: 'Jane Smith',
title: 'Senior Designer',
email: '',
role: 'Owner'
}]
const page = ref(1)
const pageCount = 5
const rows = computed(() => {
return people.slice((page.value - 1) * pageCount, (page.value) * pageCount)
})
</script>
<template>
<div>
<UTable :rows="rows" />
<div class="flex justify-end px-3 py-3.5 border-t border-gray-200 dark:border-gray-700">
<UPagination v-model="page" :page-count="pageCount" :total="people.length" />
</div>
</div>
</template>

View File

@@ -0,0 +1,80 @@
<script setup>
const columns = [{
key: 'id',
label: 'ID'
}, {
key: 'name',
label: 'Name'
}, {
key: 'title',
label: 'Title'
}, {
key: 'email',
label: 'Email'
}, {
key: 'role',
label: 'Role'
}]
const people = [{
id: 1,
name: 'Lindsay Walton',
title: 'Front-end Developer',
email: 'lindsay.walton@example.com',
role: 'Member'
}, {
id: 2,
name: 'Courtney Henry',
title: 'Designer',
email: 'courtney.henry@example.com',
role: 'Admin'
}, {
id: 3,
name: 'Tom Cook',
title: 'Director of Product',
email: 'tom.cook@example.com',
role: 'Member'
}, {
id: 4,
name: 'Whitney Francis',
title: 'Copywriter',
email: 'whitney.francis@example.com',
role: 'Admin'
}, {
id: 5,
name: 'Leonard Krasner',
title: 'Senior Designer',
email: 'leonard.krasner@example.com',
role: 'Owner'
}, {
id: 6,
name: 'Floyd Miles',
title: 'Principal Designer',
email: 'floyd.miles@example.com',
role: 'Member'
}]
const q = ref('')
const filteredRows = computed(() => {
if (!q.value) {
return people
}
return people.filter((person) => {
return Object.values(person).some((value) => {
return String(value).toLowerCase().includes(q.value.toLowerCase())
})
})
})
</script>
<template>
<div>
<div class="flex px-3 py-3.5 border-b border-gray-200 dark:border-gray-700">
<UInput v-model="q" placeholder="Filter people..." />
</div>
<UTable :rows="filteredRows" :columns="columns" />
</div>
</template>

View File

@@ -0,0 +1,45 @@
<script setup>
const people = [{
id: 1,
name: 'Lindsay Walton',
title: 'Front-end Developer',
email: 'lindsay.walton@example.com',
role: 'Member'
}, {
id: 2,
name: 'Courtney Henry',
title: 'Designer',
email: 'courtney.henry@example.com',
role: 'Admin'
}, {
id: 3,
name: 'Tom Cook',
title: 'Director of Product',
email: 'tom.cook@example.com',
role: 'Member'
}, {
id: 4,
name: 'Whitney Francis',
title: 'Copywriter',
email: 'whitney.francis@example.com',
role: 'Admin'
}, {
id: 5,
name: 'Leonard Krasner',
title: 'Senior Designer',
email: 'leonard.krasner@example.com',
role: 'Owner'
}, {
id: 6,
name: 'Floyd Miles',
title: 'Principal Designer',
email: 'floyd.miles@example.com',
role: 'Member'
}]
const selected = ref([people[1]])
</script>
<template>
<UTable v-model="selected" :rows="people" />
</template>

View File

@@ -0,0 +1,91 @@
<script setup>
const columns = [{
key: 'name',
label: 'Name'
}, {
key: 'title',
label: 'Title'
}, {
key: 'email',
label: 'Email'
}, {
key: 'role',
label: 'Role'
}, {
key: 'actions'
}]
const people = [{
id: 1,
name: 'Lindsay Walton',
title: 'Front-end Developer',
email: 'lindsay.walton@example.com',
role: 'Member'
}, {
id: 2,
name: 'Courtney Henry',
title: 'Designer',
email: 'courtney.henry@example.com',
role: 'Admin'
}, {
id: 3,
name: 'Tom Cook',
title: 'Director of Product',
email: 'tom.cook@example.com',
role: 'Member'
}, {
id: 4,
name: 'Whitney Francis',
title: 'Copywriter',
email: 'whitney.francis@example.com',
role: 'Admin'
}, {
id: 5,
name: 'Leonard Krasner',
title: 'Senior Designer',
email: 'leonard.krasner@example.com',
role: 'Owner'
}, {
id: 6,
name: 'Floyd Miles',
title: 'Principal Designer',
email: 'floyd.miles@example.com',
role: 'Member'
}]
const items = (row) => [
[{
label: 'Edit',
icon: 'i-heroicons-pencil-square-20-solid',
click: () => console.log('Edit', row.id)
}, {
label: 'Duplicate',
icon: 'i-heroicons-document-duplicate-20-solid'
}], [{
label: 'Archive',
icon: 'i-heroicons-archive-box-20-solid'
}, {
label: 'Move',
icon: 'i-heroicons-arrow-right-circle-20-solid'
}], [{
label: 'Delete',
icon: 'i-heroicons-trash-20-solid'
}]
]
const selected = ref([people[1]])
</script>
<template>
<UTable v-model="selected" :rows="people" :columns="columns">
<template #name-data="{ row }">
<span :class="[selected.find(person => person.id === row.id) && 'text-primary-500 dark:text-primary-400']">{{ row.name }}</span>
</template>
<template #actions-data="{ row }">
<UDropdown :items="items(row)">
<UButton color="gray" variant="ghost" icon="i-heroicons-ellipsis-horizontal-20-solid" />
</UDropdown>
</template>
</UTable>
</template>

View File

@@ -0,0 +1,7 @@
<script setup>
const value = ref('')
</script>
<template>
<UTextarea v-model="value" />
</template>

View File

@@ -0,0 +1,7 @@
<script setup>
const selected = ref(false)
</script>
<template>
<UToggle v-model="selected" />
</template>

View File

@@ -0,0 +1,5 @@
<template>
<UTooltip text="Tooltip example" :shortcuts="['⌘', 'O']">
<UButton color="gray" label="Hover me" />
</UTooltip>
</template>

View File

@@ -0,0 +1,25 @@
<script setup>
const links = [{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
},
badge: 100
}, {
label: 'Installation',
icon: 'i-heroicons-home',
to: '/getting-started/installation'
}, {
label: 'Vertical Navigation',
icon: 'i-heroicons-chart-bar',
to: '/navigation/vertical-navigation'
}, {
label: 'Command Palette',
icon: 'i-heroicons-command-line',
to: '/navigation/command-palette'
}]
</script>
<template>
<UVerticalNavigation :links="links" />
</template>

View File

@@ -0,0 +1,71 @@
<script lang="ts">
import { defineComponent } from '#imports'
export default defineComponent({
props: {
code: {
type: String,
default: ''
},
language: {
type: String,
default: null
},
filename: {
type: String,
default: null
},
highlights: {
type: Array as () => number[],
default: () => []
},
meta: {
type: String,
default: null
}
},
setup (props) {
const clipboard = useCopyToClipboard({ timeout: 2000 })
const icon = ref('i-heroicons-clipboard-document')
function copy () {
clipboard.copy(props.code, { title: 'Copied to clipboard!' })
icon.value = 'i-heroicons-clipboard-document-check'
setTimeout(() => {
icon.value = 'i-heroicons-clipboard-document'
}, 2000)
}
return {
icon,
copy
}
}
})
</script>
<template>
<div class="group relative" :class="`language-${language}`">
<UButton
:icon="icon"
variant="solid"
class="absolute right-3 top-3 opacity-0 group-hover:opacity-100 transition-opacity z-[1]"
size="xs"
tabindex="-1"
@click="copy"
/>
<span v-if="filename" class="text-gray-400 dark:text-gray-500 absolute right-3 bottom-3 text-sm group-hover:opacity-0 transition-opacity">{{ filename }}</span>
<slot />
</div>
</template>
<style>
pre code .line {
display: block;
min-height: 1rem;
}
</style>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
defineProps<{ id: string }>()
</script>
<template>
<h2 :id="id" class="scroll-mt-[161px] lg:scroll-mt-[112px]">
<NuxtLink :href="`#${id}`" class="group">
<div class="-ml-6 pr-2 py-2 inline-flex opacity-0 group-hover:opacity-100 transition-opacity absolute">
<UIcon name="i-heroicons-hashtag-20-solid" class="w-4 h-4 text-primary-500 dark:text-primary-400" />
</div>
<slot />
</NuxtLink>
</h2>
</template>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
defineProps<{ id: string }>()
</script>
<template>
<h3 :id="id" class="scroll-mt-[145px] lg:scroll-mt-[96px]">
<NuxtLink :href="`#${id}`" class="group">
<div class="-ml-6 pr-2 py-2 inline-flex opacity-0 group-hover:opacity-100 transition-opacity absolute">
<UIcon name="i-heroicons-hashtag-20-solid" class="w-4 h-4 text-primary-500 dark:text-primary-400" />
</div>
<slot />
</NuxtLink>
</h3>
</template>

View File

@@ -0,0 +1,76 @@
<script setup>
const commandPaletteRef = ref()
const { navigation } = useContent()
const { data: files } = await useLazyAsyncData('search', () => queryContent().where({ _type: 'markdown' }).find(), { default: () => [] })
const groups = computed(() => navigation.value.map(item => ({
key: item._path,
label: item.title,
commands: files.value.filter(file => file._path.startsWith(item._path)).map(file => ({
id: file._id,
icon: 'i-heroicons-document',
title: file.navigation?.title || file.title,
category: item.title,
to: file._path
}))
})))
const closeButton = computed(() => commandPaletteRef.value?.query ? ({ icon: 'i-heroicons-x-mark', color: 'black', variant: 'ghost', size: 'lg', padded: false }) : null)
const emptyState = computed(() => commandPaletteRef.value?.query ? ({ icon: 'i-heroicons-magnifying-glass', queryLabel: 'No results' }) : ({ icon: '', label: 'No recent searches' }))
const ui = {
wrapper: 'flex flex-col flex-1 min-h-0 bg-gray-50 dark:bg-gray-800',
input: {
wrapper: 'relative flex items-center mx-3 py-3',
base: 'w-full rounded border-2 border-primary-500 placeholder-gray-400 dark:placeholder-gray-500 focus:border-primary-500 focus:outline-none focus:ring-0 bg-white dark:bg-gray-900',
padding: 'px-4',
height: 'h-14',
size: 'text-lg',
icon: {
base: 'pointer-events-none absolute left-3 text-primary-500 dark:text-primary-400',
size: 'h-6 w-6'
}
},
group: {
wrapper: 'p-3 relative',
label: '-mx-3 px-3 -mt-4 mb-2 py-1 text-sm font-semibold text-primary-500 dark:text-primary-400 font-semibold sticky top-0 bg-gray-50 dark:bg-gray-800 z-10',
container: 'space-y-1',
command: {
base: 'flex justify-between select-none items-center rounded px-2 py-4 gap-2 relative font-medium text-sm group shadow',
active: 'bg-primary-500 dark:bg-primary-400 text-white',
inactive: 'bg-white dark:bg-gray-900',
label: 'flex flex-col min-w-0',
suffix: 'text-xs',
icon: {
base: 'flex-shrink-0 w-6 h-6',
active: 'text-white',
inactive: 'text-gray-400 dark:text-gray-500'
}
}
},
emptyState: {
wrapper: 'flex flex-col items-center justify-center flex-1 py-9',
label: 'text-sm text-center text-gray-500 dark:text-gray-400',
queryLabel: 'text-lg text-center text-gray-900 dark:text-white',
icon: 'w-12 h-12 mx-auto text-gray-400 dark:text-gray-500 mb-4'
}
}
</script>
<template>
<UCommandPalette
ref="commandPaletteRef"
:groups="groups"
:ui="ui"
:close-button="closeButton"
:empty-state="emptyState"
:autoselect="false"
command-attribute="title"
:fuse="{
fuseOptions: { keys: ['title', 'category'] },
}"
placeholder="Search docs"
/>
</template>

View File

@@ -0,0 +1,64 @@
<script setup>
const commandPaletteRef = ref()
const suggestions = [
{ id: 'linear', label: 'Linear', icon: 'i-simple-icons-linear' },
{ id: 'figma', label: 'Figma', icon: 'i-simple-icons-figma' },
{ id: 'slack', label: 'Slack', icon: 'i-simple-icons-slack' },
{ id: 'youtube', label: 'YouTube', icon: 'i-simple-icons-youtube' },
{ id: 'github', label: 'GitHub', icon: 'i-simple-icons-github' }
]
const commands = [
{ id: 'clipboard-history', label: 'Clipboard History', icon: 'i-heroicons-clipboard', click: () => alert('New file') },
{ id: 'import-extension', label: 'Import Extension', icon: 'i-heroicons-wrench-screwdriver', click: () => alert('New folder') },
{ id: 'manage-extensions', label: 'Manage Extensions', icon: 'i-heroicons-wrench-screwdriver', click: () => alert('Add hashtag') }
]
const groups = [{
key: 'suggestions',
label: 'Suggestions',
inactive: 'Application',
commands: suggestions
}, {
key: 'commands',
label: 'Commands',
inactive: 'Command',
commands
}]
const ui = {
wrapper: 'flex flex-col flex-1 min-h-0 divide-y divide-gray-200 dark:divide-gray-700 bg-gray-50 dark:bg-gray-800',
container: 'relative flex-1 overflow-y-auto divide-y divide-gray-200 dark:divide-gray-700 scroll-py-2',
input: {
base: 'w-full h-14 px-4 placeholder-gray-400 dark:placeholder-gray-500 bg-transparent border-0 text-gray-900 dark:text-white focus:ring-0'
},
group: {
label: 'px-2 my-2 text-xs font-semibold text-gray-500 dark:text-gray-400',
command: {
base: 'flex justify-between select-none cursor-default items-center rounded-md px-2 py-2 gap-2 relative',
active: 'bg-gray-200 dark:bg-gray-700/50 text-gray-900 dark:text-white',
container: 'flex items-center gap-3 min-w-0',
icon: {
base: 'flex-shrink-0 w-5 h-5',
active: 'text-gray-900 dark:text-white',
inactive: 'text-gray-400 dark:text-gray-500'
},
avatar: {
size: '2xs'
}
}
}
}
</script>
<template>
<UCommandPalette
ref="commandPaletteRef"
:groups="groups"
icon=""
:ui="ui"
:autoselect="false"
placeholder="Search for apps and commands"
/>
</template>

View File

@@ -0,0 +1,21 @@
<script setup>
const page = ref(1)
const items = ref(Array(55))
</script>
<template>
<UPagination
v-model="page"
:total="items.length"
:ui="{
wrapper: 'flex items-center gap-1',
rounded: 'rounded-full min-w-[32px] justify-center'
}"
:prev-button="null"
:next-button="{
icon: 'i-heroicons-arrow-small-right-20-solid',
color: 'primary',
variant: 'outline'
}"
/>
</template>

View File

@@ -0,0 +1,28 @@
<script setup>
const links = [{
label: 'Installation',
to: '/getting-started/installation'
}, {
label: 'Vertical Navigation',
to: '/navigation/vertical-navigation'
}, {
label: 'Command Palette',
to: '/navigation/command-palette'
}]
</script>
<template>
<UVerticalNavigation
:links="links"
:ui="{
wrapper: 'border-l border-gray-200 dark:border-gray-800 space-y-2',
base: 'group block border-l -ml-px lg:leading-6',
padding: 'pl-4',
rounded: '',
font: '',
ring: '',
active: 'text-primary-500 dark:text-primary-400 border-current font-semibold',
inactive: 'border-transparent hover:border-gray-400 dark:hover:border-gray-500 text-gray-700 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300'
}"
/>
</template>

View File

@@ -0,0 +1,32 @@
<template>
<aside class="hidden pb-8 overflow-y-auto lg:block lg:self-start lg:top-16 lg:max-h-[calc(100vh-64px)] lg:sticky lg:pr-8 lg:pl-[2px]">
<div class="relative">
<div class="sticky top-0 pointer-events-none z-[1]">
<div class="h-8 bg-white dark:bg-gray-900" />
<div class="bg-white dark:bg-gray-900 relative pointer-events-auto">
<UButton
icon="i-heroicons-magnifying-glass-20-solid"
class="w-full"
color="gray"
@click="isSearchModalOpen = true"
>
Search
<div class="hidden lg:flex items-center gap-0.5 ml-auto -my-1">
<UKbd>{{ metaSymbol }}</UKbd>
<UKbd>K</UKbd>
</div>
</UButton>
</div>
<div class="h-8 bg-gradient-to-b from-white dark:from-gray-900" />
</div>
<DocsAsideLinks />
</div>
</aside>
</template>
<script setup lang="ts">
const { isSearchModalOpen } = useDocs()
const { metaSymbol } = useShortcuts()
</script>

View File

@@ -0,0 +1,40 @@
<template>
<div class="space-y-8">
<div v-for="(group, index) in navigation" :key="index" class="space-y-3">
<div class="text-sm font-semibold text-gray-900 dark:text-gray-200">
<span class="truncate">{{ group.title }}</span>
</div>
<UVerticalNavigation
:links="mapContentLinks(group.children)"
class="mt-1"
:ui="{
wrapper: 'border-l border-gray-200 dark:border-gray-800 space-y-2',
base: 'group block border-l -ml-px lg:leading-6 flex items-center gap-2',
padding: 'pl-4',
rounded: '',
font: '',
ring: '',
active: 'text-primary-500 dark:text-primary-400 border-current',
inactive: 'border-transparent hover:border-gray-400 dark:hover:border-gray-500 text-gray-700 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300'
}"
>
<template #badge="{ link }">
<UBadge v-if="link.badge" size="xs" :ui="{ rounded: 'rounded-full' }">
{{ link.badge }}
</UBadge>
</template>
</UVerticalNavigation>
</div>
</div>
</template>
<script setup lang="ts">
import type { NavItem } from '@nuxt/content/dist/runtime/types'
const { navigation } = useContent() as { navigation: NavItem[] }
function mapContentLinks (links: NavItem[]) {
return links?.map(link => ({ label: link.title, icon: link.icon, to: link._path, badge: link.badge })) || []
}
</script>

View File

@@ -0,0 +1,10 @@
<template>
<footer class="flex items-center justify-end gap-1.5">
<div class="flex items-baseline gap-1.5 text-sm text-center text-gray-500 dark:text-gray-400">
Made by
<NuxtLink to="https://nuxtlabs.com" aria-label="NuxtLabs">
<LogoLabs class="text-primary-500 w-14 h-auto dark:text-primary-400" />
</NuxtLink>
</div>
</footer>
</template>

View File

@@ -0,0 +1,17 @@
<template>
<div class="flex items-center justify-between gap-1.5">
<UButton
v-if="page"
:to="`https://github.com/nuxtlabs/ui/edit/dev/docs/content/${page._file}`"
label="Edit this page on GitHub"
color="primary"
variant="link"
:padded="false"
icon="i-heroicons-pencil-square"
/>
</div>
</template>
<script setup lang="ts">
const { page } = useContent()
</script>

View File

@@ -0,0 +1,37 @@
<template>
<header v-if="page" class="relative border-b border-gray-200 dark:border-gray-800 pb-8 mb-12">
<p class="mb-4 text-sm leading-6 font-semibold text-primary-500 dark:text-primary-400 capitalize">
{{ page._dir?.title ? page._dir.title : useLowerCase(page._dir) }}
</p>
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between">
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 tracking-tight dark:text-white">
{{ page.title }}
</h1>
<div v-if="page.headlessui || page.github" class="flex items-center gap-2 mt-4 lg:mt-0">
<UButton
v-if="page.headlessui"
:label="page.headlessui.label"
:to="page.headlessui.to"
icon="i-simple-icons-headlessui"
color="white"
/>
<UButton
v-if="page.github"
label="GitHub"
icon="i-simple-icons-github"
color="white"
:to="`https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/${page._dir}/${page.title.replace(' ', '')}${page.github.suffix || '.vue'}`"
/>
</div>
</div>
<p v-if="page.description" class="mt-4 text-lg">
{{ page.description }}
</p>
</header>
</template>
<script setup lang="ts">
const { page } = useContent()
</script>

View File

@@ -0,0 +1,18 @@
<template>
<div class="grid gap-6 sm:grid-cols-2">
<DocsPrevNextCard v-if="prev" :title="prev.navigation?.title || prev.title" :description="prev.navigation?.description || prev.description" :to="prev._path" icon="i-heroicons-arrow-left-20-solid" />
<span v-else class="hidden sm:block">&nbsp;</span>
<DocsPrevNextCard
v-if="next"
:title="next.navigation?.title || next.title"
:description="next.navigation?.description || next.description"
:to="next._path"
icon="i-heroicons-arrow-right-20-solid"
class="text-right"
/>
</div>
</template>
<script setup lang="ts">
const { prev, next } = useContent()
</script>

View File

@@ -0,0 +1,36 @@
<template>
<NuxtLink :to="to" class="block px-5 py-8 border not-prose rounded-lg border-gray-200 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800/25 group">
<div v-if="icon" class="inline-flex items-center rounded-full p-1.5 bg-gray-50 dark:bg-gray-800 group-hover:bg-primary-50 dark:group-hover:bg-primary-400/10 ring-1 ring-gray-300 dark:ring-gray-700 mb-4 group-hover:ring-primary-500/50 dark:group-hover:ring-primary-400/50">
<UIcon :name="icon" class="w-5 h-5 text-gray-900 dark:text-gray-100 group-hover:text-primary-500 dark:group-hover:text-primary-400" />
</div>
<p class="text-gray-900 dark:text-gray-50 font-medium text-[15px] mb-1">
{{ title }}
</p>
<p class="text-sm font-normal text-gray-500 dark:text-gray-400">
{{ description }}
</p>
</NuxtLink>
</template>
<script setup lang="ts">
defineProps({
icon: {
type: String,
default: null
},
title: {
type: String,
default: ''
},
description: {
type: String,
default: ''
},
to: {
type: String,
required: true
}
})
</script>

View File

@@ -0,0 +1,172 @@
<template>
<UModal
v-model="isSearchModalOpen"
:ui="{
padding: 'sm:p-4',
rounded: 'sm:rounded-lg',
width: 'sm:max-w-3xl',
height: 'h-screen sm:h-[28rem]'
}"
>
<UCommandPalette
ref="commandPaletteRef"
:groups="groups"
command-attribute="title"
:fuse="{
fuseOptions: { ignoreLocation: true, includeMatches: true, threshold: 0, keys: ['title', 'description', 'children.children.value', 'children.children.children.value'] },
resultLimit: 10
}"
@update:model-value="onSelect"
@close="isSearchModalOpen = false"
/>
</UModal>
</template>
<script setup lang="ts">
import type { Command } from '../../../src/runtime/types'
const { navigation } = useContent()
const router = useRouter()
const { usingInput } = useShortcuts()
const { isSearchModalOpen } = useDocs()
const commandPaletteRef = ref<HTMLElement & { query: Ref<string>, results: { item: Command }[] }>()
const { data: files } = await useLazyAsyncData('search', () => queryContent().where({ _type: 'markdown' }).find(), { default: () => [] })
// Computed
const defaultGroups = computed(() => navigation.value.map(item => ({
key: item._path,
label: item.title,
commands: files.value.filter(file => file._path.startsWith(item._path)).map(file => ({
id: file._id,
title: file.navigation?.title || file.title,
to: file._path,
suffix: file.description,
icon: file.icon
}))
})))
const queryGroups = computed(() => navigation.value.map(item => ({
key: item._path,
label: item.title,
commands: files.value.filter(file => file._path.startsWith(item._path)).flatMap((file) => {
return [{
id: file._id,
title: file.navigation?.title || file.title,
to: file._path,
description: file.description,
icon: file.icon
},
// @ts-ignore
...Object.entries(groupByHeading(file.body.children)).map(([hash, { title, children }]) => ({
id: `${file._path}${hash}`,
title,
prefix: `${file.navigation?.title || file.title} ->`,
prefixClass: 'text-gray-700 dark:text-gray-200',
to: `${file._path}${hash}`,
children: concatChildren(children),
icon: file.icon
}))]
})
})))
const groups = computed(() => commandPaletteRef.value?.query ? queryGroups.value : defaultGroups.value)
// avoid conflicts between multiple meta_k shortcuts
const canToggleModal = computed(() => isSearchModalOpen.value || !usingInput.value)
// Methods
function remapChildren (children: any[]) {
return children?.map((grandChild) => {
if (['code-inline', 'em', 'a', 'strong'].includes(grandChild.tag)) {
return { type: 'text', value: grandChild.children.find(child => child.type === 'text')?.value || '' }
}
return grandChild
})
}
function concatChildren (children: any[]) {
return children.map((child) => {
if (['alert'].includes(child.tag)) {
child.children = concatChildren(child.children)
}
if (child.tag === 'p') {
child.children = remapChildren(child.children)
child.children = child.children?.reduce((acc, grandChild) => {
if (grandChild.type === 'text') {
if (acc.length && acc[acc.length - 1].type === 'text') {
acc[acc.length - 1].value += grandChild.value
} else {
acc.push(grandChild)
}
} else {
acc.push(grandChild)
}
return acc
}, [])
}
if (['style'].includes(child.tag)) {
return null
}
return child
})
}
function groupByHeading (children: any[]) {
const groups = {} // grouped by path
let hash = '' // file.page with potential `#anchor` concat
let title: string | null
for (const node of children) {
// if heading found, udpate current path
if (['h2', 'h3'].includes(node.tag)) {
// find heading text value
title = node.children?.find(child => child.type === 'text')?.value
if (title) {
hash = `#${node.props.id}`
}
}
// push to existing/new group based on path
if (groups[hash]) {
groups[hash].children.push(node)
} else {
groups[hash] = { children: [node], title }
}
}
return groups
}
function onSelect (option) {
isSearchModalOpen.value = false
if (option.click) {
option.click()
} else if (option.to) {
router.push(option.to)
} else if (option.href) {
window.open(option.href, '_blank')
}
}
// Shortcuts
defineShortcuts({
meta_k: {
usingInput: true,
whenever: [canToggleModal],
handler: () => {
isSearchModalOpen.value = !isSearchModalOpen.value
}
},
escape: {
usingInput: true,
whenever: [isSearchModalOpen],
handler: () => { isSearchModalOpen.value = false }
}
})
</script>

View File

@@ -0,0 +1,19 @@
<template>
<div class="sticky top-16 bg-white/75 dark:bg-gray-900/75 backdrop-blur group lg:self-start -mx-4 sm:-mx-6 lg:mx-0 px-4 sm:px-6 lg:pl-8 lg:pr-0">
<div class="py-3 lg:py-8 border-b border-dashed border-gray-200 dark:border-gray-800 lg:border-0">
<button class="flex items-center gap-2" tabindex="-1" @click="isTocOpen = !isTocOpen">
<span class="text-sm text-slate-900 font-semibold text-sm leading-6 dark:text-slate-100 truncate">Table of Contents</span>
<UIcon name="i-heroicons-chevron-right-20-solid" class="lg:hidden w-4 h-4 transition-transform duration-100 transform text-gray-400 dark:text-gray-500" :class="[isTocOpen ? 'rotate-90' : 'rotate-0']" />
</button>
<DocsTocLinks class="mt-2 lg:mt-4" :links="toc.links" :class="[isTocOpen ? 'lg:block' : 'hidden lg:block']" />
</div>
</div>
</template>
<script setup lang="ts">
const { toc } = useContent()
const isTocOpen = ref(false)
</script>

View File

@@ -0,0 +1,49 @@
<template>
<ul>
<li v-for="link in links" :key="link.text" :class="{ 'ml-3': link.depth === 3 }">
<a
:href="`#${link.id}`"
class="block py-1 font-medium text-sm"
:class="[activeHeadings.includes(link.id) ? 'text-primary-500 dark:text-primary-400' : 'hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300']"
@click.prevent="scrollToHeading(link.id)"
>
{{ link.text }}
</a>
<DocsTocLinks v-if="link.children" :links="link.children" />
</li>
</ul>
</template>
<script setup lang="ts">
import type { TocLink } from '@nuxt/content/dist/runtime/types'
defineProps({
links: {
type: Array as PropType<TocLink[]>,
default: () => []
}
})
const emit = defineEmits(['move'])
const route = useRoute()
const router = useRouter()
const { activeHeadings, updateHeadings } = useScrollspy()
watch(() => route.path, () => {
setTimeout(() => {
if (process.client) {
updateHeadings([
...document.querySelectorAll('h2'),
...document.querySelectorAll('h3')
])
}
}, 300)
}, { immediate: true })
const scrollToHeading = (id: string) => {
router.push(`#${id}`)
emit('move', id)
}
</script>

View File

@@ -0,0 +1,28 @@
const useComponentsMetaState = () => useState('components-meta', () => ({}))
export async function fetchComponentMeta (name: string) {
const state = useComponentsMetaState()
if (state.value[name]?.then) {
await state.value[name]
return state.value[name]
}
if (state.value[name]) { return state.value[name] }
// Store promise to avoid multiple calls
// add to nitro prerender
if (process.server) {
const event = useRequestEvent()
event.node.res.setHeader(
'x-nitro-prerender',
[event.node.res.getHeader('x-nitro-prerender'), `/api/component-meta/${name}.json`].filter(Boolean).join(',')
)
}
state.value[name] = $fetch(`/api/component-meta/${name}.json`).then((meta) => {
state.value[name] = meta
})
await state.value[name]
return state.value[name]
}

View File

@@ -0,0 +1,11 @@
import { createSharedComposable } from '@vueuse/core'
const _useDocs = () => {
const isSearchModalOpen = ref(false)
return {
isSearchModalOpen
}
}
export const useDocs = createSharedComposable(_useDocs)

View File

@@ -0,0 +1,37 @@
/**
* Scrollspy allows you to watch visible headings in a specific page.
* Useful for table of contents live style updates.
*/
export const useScrollspy = () => {
const observer = ref() as Ref<IntersectionObserver>
const visibleHeadings = ref([]) as Ref<string[]>
const activeHeadings = ref([]) as Ref<string[]>
const observerCallback = (entries: IntersectionObserverEntry[]) =>
entries.forEach((entry) => {
const id = entry.target.id
if (entry.isIntersecting) { visibleHeadings.value.push(id) } else { visibleHeadings.value = visibleHeadings.value.filter(t => t !== id) }
})
const updateHeadings = (headings: Element[]) =>
headings.forEach((heading) => {
observer.value.observe(heading)
})
watch(visibleHeadings, (val, oldVal) => {
if (val.length === 0) { activeHeadings.value = oldVal } else { activeHeadings.value = val }
})
// Create intersection observer
onBeforeMount(() => (observer.value = new IntersectionObserver(observerCallback)))
// Destroy it
onBeforeUnmount(() => observer.value?.disconnect())
return {
visibleHeadings,
activeHeadings,
updateHeadings
}
}

View File

@@ -0,0 +1,36 @@
---
title: Introduction
description: 'Fully styled and customizable components for Nuxt.'
head:
title: 'NuxtLabs UI: Fully styled and customizable components for Nuxt'
description: 'It provides everything related to UI when building your Nuxt app. This includes components, icons, colors, dark mode but also keyboard shortcuts. Built with Headless UI and Tailwind CSS, published under MIT License.'
---
This module has been developed by the [NuxtLabs](https://nuxtlabs.com/) team for [Volta](https://volta.net) and [Nuxt Studio](https://nuxt.studio/), its goal is to provide everything related to UI when building a Nuxt app. This includes components, icons, colors, dark mode but also keyboard shortcuts.
::alert{icon="i-heroicons-light-bulb"}
[Volta](https://volta.net/) entire UI is built with this module alongside the 50+ keyboard shortcuts defined.
::
## Features
- Built with [Headless UI](https://headlessui.dev/) and [Tailwind CSS](https://tailwindcss.com/)
- HMR support through Nuxt App Config
- Dark mode support
- Keyboard shortcuts
- Bundled icons
- Fully typed
## Credits
- [nuxt/nuxt](https://github.com/nuxt/nuxt)
- [nuxt-modules/color-mode](https://github.com/nuxt-modules/color-mode)
- [nuxt-modules/tailwindcss](https://github.com/nuxt-modules/tailwindcss)
- [tailwindlabs/tailwindcss](https://github.com/tailwindlabs/tailwindcss)
- [tailwindlabs/headlessui](https://github.com/tailwindlabs/headlessui)
- [vueuse/vueuse](https://github.com/vueuse/vueuse)
- [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons)
::alert{icon="i-heroicons-exclamation-triangle"}
This documentation is still a work in progress and will be updated regularly.
::

View File

@@ -0,0 +1,64 @@
---
description: 'Learn how to install and configure the module in your Nuxt app.'
---
1. Install `@nuxthq/ui` dependency to your project:
::code-group
```bash [yarn]
yarn add -D @nuxthq/ui
```
```bash [npm]
npm install -D @nuxthq/ui
```
```sh [pnpm]
pnpm i -D @nuxthq/ui
```
::
2. Add it to your `modules` section in your `nuxt.config`:
```ts [nuxt.config]
export default defineNuxtConfig({
modules: ['@nuxthq/ui']
})
```
That's it! You can now use all the components and composables in your Nuxt app ✨
::alert{icon="i-heroicons-exclamation-triangle"}
As this module installs [@nuxtjs/tailwindcss](https://tailwindcss.nuxtjs.org/) and [@nuxtjs/color-mode](https://color-mode.nuxtjs.org/) for you, you should remove them from your `modules` and `dependencies` if you've previously installed them manually.
::
## Playground
:u-button{icon="i-simple-icons-stackblitz" label="Play on StackBlitz" size="lg" to="https://stackblitz.com/edit/nuxtlabs-ui?file=app.config.ts,app.vue" target="_blank"}
## Options
| Key | Default | Description |
| ------------------------ | ---------------------- | ------------------------------------------------ |
| `prefix` | `u` | Define the prefix of the imported components. |
| `global` | `false` | Expose components globally. |
| `icons` | `['heroicons']` | Icon collections to load. |
| `safelistColors` | `['primary']` | Force safelisting of colors. |
## Edge
To use the latest updates pushed on the [`dev`](https://github.com/nuxtlabs/ui/tree/dev) branch, you can use `@nuxthq/ui-edge`.
Update your `package.json` to the following:
```json [package.json]
{
"devDependencies": {
"@nuxthq/ui": "npm:@nuxthq/ui-edge@latest"
}
}
```
Then run `npm install` or `yarn install`.

Some files were not shown because too many files have changed in this diff Show More