@sviy/ui 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -2
- package/index.ts +61 -0
- package/package.json +44 -4
- package/src/components/ui/alert/Alert.vue +21 -0
- package/src/components/ui/alert/AlertAction.vue +17 -0
- package/src/components/ui/alert/AlertDescription.vue +17 -0
- package/src/components/ui/alert/AlertTitle.vue +17 -0
- package/src/components/ui/alert/index.ts +21 -0
- package/src/components/ui/avatar/Avatar.vue +22 -0
- package/src/components/ui/avatar/AvatarBadge.vue +23 -0
- package/src/components/ui/avatar/AvatarFallback.vue +21 -0
- package/src/components/ui/avatar/AvatarGroup.vue +20 -0
- package/src/components/ui/avatar/AvatarGroupCount.vue +20 -0
- package/src/components/ui/avatar/AvatarImage.vue +16 -0
- package/src/components/ui/avatar/index.ts +27 -0
- package/src/components/ui/badge/Badge.vue +27 -0
- package/src/components/ui/badge/index.ts +24 -0
- package/src/components/ui/button/Button.vue +31 -0
- package/src/components/ui/button/index.ts +35 -0
- package/src/components/ui/card/Card.vue +21 -0
- package/src/components/ui/card/CardAction.vue +17 -0
- package/src/components/ui/card/CardContent.vue +17 -0
- package/src/components/ui/card/CardDescription.vue +17 -0
- package/src/components/ui/card/CardFooter.vue +17 -0
- package/src/components/ui/card/CardHeader.vue +17 -0
- package/src/components/ui/card/CardTitle.vue +17 -0
- package/src/components/ui/card/index.ts +7 -0
- package/src/components/ui/checkbox/Checkbox.vue +34 -0
- package/src/components/ui/checkbox/index.ts +1 -0
- package/src/components/ui/dialog/Dialog.vue +19 -0
- package/src/components/ui/dialog/DialogClose.vue +15 -0
- package/src/components/ui/dialog/DialogContent.vue +53 -0
- package/src/components/ui/dialog/DialogDescription.vue +23 -0
- package/src/components/ui/dialog/DialogFooter.vue +27 -0
- package/src/components/ui/dialog/DialogHeader.vue +17 -0
- package/src/components/ui/dialog/DialogOverlay.vue +21 -0
- package/src/components/ui/dialog/DialogScrollContent.vue +60 -0
- package/src/components/ui/dialog/DialogTitle.vue +23 -0
- package/src/components/ui/dialog/DialogTrigger.vue +15 -0
- package/src/components/ui/dialog/index.ts +10 -0
- package/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +43 -0
- package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +40 -0
- package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
- package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
- package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +44 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +32 -0
- package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/src/components/ui/dropdown-menu/index.ts +16 -0
- package/src/components/ui/input/Input.vue +31 -0
- package/src/components/ui/input/index.ts +1 -0
- package/src/components/ui/label/Label.vue +26 -0
- package/src/components/ui/label/index.ts +1 -0
- package/src/components/ui/progress/Progress.vue +38 -0
- package/src/components/ui/progress/index.ts +1 -0
- package/src/components/ui/select/Select.vue +19 -0
- package/src/components/ui/select/SelectContent.vue +58 -0
- package/src/components/ui/select/SelectGroup.vue +21 -0
- package/src/components/ui/select/SelectItem.vue +45 -0
- package/src/components/ui/select/SelectItemText.vue +15 -0
- package/src/components/ui/select/SelectLabel.vue +17 -0
- package/src/components/ui/select/SelectScrollDownButton.vue +27 -0
- package/src/components/ui/select/SelectScrollUpButton.vue +27 -0
- package/src/components/ui/select/SelectSeparator.vue +19 -0
- package/src/components/ui/select/SelectTrigger.vue +34 -0
- package/src/components/ui/select/SelectValue.vue +15 -0
- package/src/components/ui/select/index.ts +11 -0
- package/src/components/ui/separator/Separator.vue +29 -0
- package/src/components/ui/separator/index.ts +1 -0
- package/src/components/ui/sonner/Sonner.vue +62 -0
- package/src/components/ui/sonner/index.ts +1 -0
- package/src/components/ui/switch/Switch.vue +44 -0
- package/src/components/ui/switch/index.ts +1 -0
- package/src/components/ui/table/Table.vue +16 -0
- package/src/components/ui/table/TableBody.vue +17 -0
- package/src/components/ui/table/TableCaption.vue +17 -0
- package/src/components/ui/table/TableCell.vue +17 -0
- package/src/components/ui/table/TableEmpty.vue +34 -0
- package/src/components/ui/table/TableFooter.vue +17 -0
- package/src/components/ui/table/TableHead.vue +17 -0
- package/src/components/ui/table/TableHeader.vue +17 -0
- package/src/components/ui/table/TableRow.vue +17 -0
- package/src/components/ui/table/index.ts +9 -0
- package/src/components/ui/tabs/Tabs.vue +25 -0
- package/src/components/ui/tabs/TabsContent.vue +21 -0
- package/src/components/ui/tabs/TabsList.vue +29 -0
- package/src/components/ui/tabs/TabsTrigger.vue +29 -0
- package/src/components/ui/tabs/index.ts +24 -0
- package/src/components/ui/textarea/Textarea.vue +28 -0
- package/src/components/ui/textarea/index.ts +1 -0
- package/src/components/ui/tooltip/Tooltip.vue +19 -0
- package/src/components/ui/tooltip/TooltipContent.vue +34 -0
- package/src/components/ui/tooltip/TooltipProvider.vue +14 -0
- package/src/components/ui/tooltip/TooltipTrigger.vue +15 -0
- package/src/components/ui/tooltip/index.ts +4 -0
- package/src/lib/utils.ts +1 -0
- package/theme.css +223 -0
package/README.md
CHANGED
|
@@ -1,3 +1,143 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sviy/ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
House UI kit. Reka UI primitives, styled with Tailwind v4, generated by the
|
|
4
|
+
`shadcn-vue` CLI and owned by us. No upstream, no build step.
|
|
5
|
+
|
|
6
|
+
Ships **raw `.vue`/`.ts` source**. The app's bundler compiles it. Installed from
|
|
7
|
+
npm; nothing is ever copied into an app.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Consuming it
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @sviy/ui vue tailwindcss @tailwindcss/vite @lucide/vue
|
|
15
|
+
npm install -D typescript
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`vue`, `tailwindcss`, `@lucide/vue` are peers — the app owns those versions.
|
|
19
|
+
`typescript` is required in the **app**: the kit is typed `.vue` source and
|
|
20
|
+
`@vue/compiler-sfc` cannot resolve imported types without it. Omitting it fails
|
|
21
|
+
the build with `Failed to load TypeScript…`.
|
|
22
|
+
|
|
23
|
+
**`vite.config.ts`** — `optimizeDeps.exclude` is mandatory:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
plugins: [vue(), tailwindcss()],
|
|
28
|
+
optimizeDeps: { exclude: ['@sviy/ui'] }, // raw source: esbuild must not pre-bundle it
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**CSS entry** — two lines, in this order:
|
|
33
|
+
|
|
34
|
+
```css
|
|
35
|
+
@import "tailwindcss";
|
|
36
|
+
@import "@sviy/ui/theme.css";
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Do **not** add an `@source` directive. `theme.css` carries its own
|
|
40
|
+
`@source "./src"`, resolved against the installed copy.
|
|
41
|
+
|
|
42
|
+
**Imports** — always from the package root. Never from `@sviy/ui/src/…`:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { Button, Card, Dialog, Table, toast, Toaster, icons } from '@sviy/ui'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Dark mode** is class-based, not `prefers-color-scheme`. The app toggles `.dark`
|
|
49
|
+
on `<html>`:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
document.documentElement.classList.toggle('dark', dark.value)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Mount `<Toaster />` once near the app root. Wrap anything using `Tooltip` in
|
|
56
|
+
`TooltipProvider`.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Surface
|
|
61
|
+
|
|
62
|
+
`alert` · `avatar` · `badge` · `button` · `card` · `checkbox` · `dialog` ·
|
|
63
|
+
`dropdown-menu` · `input` · `label` · `progress` · `select` · `separator` ·
|
|
64
|
+
`sonner` · `switch` · `table` · `tabs` · `textarea` · `tooltip`
|
|
65
|
+
|
|
66
|
+
Plus `cn`, `toast`, `icons`.
|
|
67
|
+
|
|
68
|
+
- **Icons** are only under the `icons` namespace: `<icons.PlusIcon class="size-4" />`.
|
|
69
|
+
`export * from '@lucide/vue'` is **not** re-exported flat — `Badge` and `Table`
|
|
70
|
+
exist in both, and ESM silently drops names supplied by two star-exports, which
|
|
71
|
+
would break `import { Badge }`. Import `@lucide/vue` directly if you prefer flat.
|
|
72
|
+
- **`Table` is primitives only.** Sorting/filtering are app policy — write a
|
|
73
|
+
`computed`.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Adding a component
|
|
78
|
+
|
|
79
|
+
Run the CLI **in place**, from the kit repo root:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npx --yes shadcn-vue@latest add <component> -y
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Then:
|
|
86
|
+
|
|
87
|
+
1. **Rewrite `@/…` imports to relative paths.** A component importing a sibling
|
|
88
|
+
family (e.g. `DialogContent.vue` → `Button`) is generated as
|
|
89
|
+
`@/components/ui/button`, which cannot resolve in a published package. Use
|
|
90
|
+
`../button`. Step 3 enforces this — `tsconfig.json` maps no `@/*` path.
|
|
91
|
+
2. **Export the family** from `index.ts` (hand-maintained).
|
|
92
|
+
3. **`npm run typecheck`** — `vue-tsc`. The kit's only gate; an app's JS build
|
|
93
|
+
never checks these SFCs.
|
|
94
|
+
4. **Release** (below). Unpublished means invisible.
|
|
95
|
+
|
|
96
|
+
### Rules
|
|
97
|
+
|
|
98
|
+
- **Bare `data-*` variants do not exist in Tailwind v4.** `data-open:` compiles to
|
|
99
|
+
the literal selector `[data-open]`, but Reka writes `data-state` /
|
|
100
|
+
`data-orientation`. The names the components need are mapped once in
|
|
101
|
+
`theme.css` under "Reka attribute variants": `data-open`, `data-closed`,
|
|
102
|
+
`data-checked`, `data-unchecked`, `data-horizontal`, `data-vertical`.
|
|
103
|
+
Any generated bare variant **outside that list** needs its own
|
|
104
|
+
`@custom-variant` there. **This failure is silent** — the build succeeds and the
|
|
105
|
+
CSS never matches. Check what Reka really emits with:
|
|
106
|
+
`grep -rho '"data-[a-z-]*"' node_modules/reka-ui/dist`
|
|
107
|
+
- **`dev.css` is a generator target, not published.** The CLI rewrites it on every
|
|
108
|
+
run. Leave it alone.
|
|
109
|
+
- **Check dependencies the CLI adds.** A new one may belong in `peerDependencies`.
|
|
110
|
+
- **Fix hardcoded colour/font in a new component in `theme.css` §3**, never in an app.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Releasing
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm version patch
|
|
118
|
+
npm publish --access public
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`npm version` edits `package.json`, commits and tags in one step — let it do the
|
|
122
|
+
edit so the three can never disagree. It aborts on a dirty tree: commit first.
|
|
123
|
+
`--access public` is required; scoped packages default to private.
|
|
124
|
+
|
|
125
|
+
**A version bump is mandatory.** The registry is immutable — one publish per
|
|
126
|
+
version, re-publishing is rejected. Apps resolve by version, so a committed but
|
|
127
|
+
unversioned change is uninstallable.
|
|
128
|
+
|
|
129
|
+
Consumers: `npm install @sviy/ui@latest`.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Brand
|
|
134
|
+
|
|
135
|
+
All colour lives in `theme.css`, in three sections. Nothing else defines it.
|
|
136
|
+
|
|
137
|
+
1. `:root` — house tokens (`--primary`, `--background`, `--radius`, …)
|
|
138
|
+
2. `.dark` — dark counterparts, **same token names**
|
|
139
|
+
3. Third-party corrections
|
|
140
|
+
|
|
141
|
+
Rebranding edits §1 and §2 only — never a component, never an app. The typeface
|
|
142
|
+
is IBM Plex Sans, self-hosted via `@fontsource` (no runtime CDN request), set once
|
|
143
|
+
on `--font-sans`.
|
package/index.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sviy/ui — the single entry point for apps.
|
|
3
|
+
*
|
|
4
|
+
* Apps import everything from here and never touch the component folders, so
|
|
5
|
+
* the components can be upgraded without every app being edited.
|
|
6
|
+
*
|
|
7
|
+
* import { Button, Dialog, Table, cn } from '@sviy/ui';
|
|
8
|
+
*
|
|
9
|
+
* Each family is its own module, so only what an app actually imports ends up
|
|
10
|
+
* in its bundle.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export * from './src/lib/utils'
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* The Toaster component is in the kit, so the function that drives it should be
|
|
17
|
+
* too — otherwise every app has to depend on `vue-sonner` directly just to push
|
|
18
|
+
* a notification.
|
|
19
|
+
*/
|
|
20
|
+
export { toast } from 'vue-sonner'
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* Icons, under a namespace.
|
|
24
|
+
*
|
|
25
|
+
* The obvious form, `export * from '@lucide/vue'`, breaks the kit: `Badge` and
|
|
26
|
+
* `Table` exist both as kit components and as lucide icons, and ESM silently
|
|
27
|
+
* drops any name that two star-exports provide. The failure then surfaces in
|
|
28
|
+
* the *app* as `"Badge" is not exported by "@sviy/ui"`, with nothing pointing
|
|
29
|
+
* back here. It is also a moving target — the day lucide ships a `Card` or
|
|
30
|
+
* `Button` icon, the kit quietly loses that component.
|
|
31
|
+
*
|
|
32
|
+
* A namespace cannot collide with anything, and it costs nothing: the built
|
|
33
|
+
* bundle is byte-identical to not exporting icons at all, because only the
|
|
34
|
+
* icons an app actually references survive tree-shaking.
|
|
35
|
+
*
|
|
36
|
+
* import { icons } from '@sviy/ui'
|
|
37
|
+
* <icons.PlusIcon />
|
|
38
|
+
*
|
|
39
|
+
* Apps that prefer the flat form can still import from '@lucide/vue' directly.
|
|
40
|
+
*/
|
|
41
|
+
export * as icons from '@lucide/vue'
|
|
42
|
+
|
|
43
|
+
export * from './src/components/ui/alert'
|
|
44
|
+
export * from './src/components/ui/avatar'
|
|
45
|
+
export * from './src/components/ui/badge'
|
|
46
|
+
export * from './src/components/ui/button'
|
|
47
|
+
export * from './src/components/ui/card'
|
|
48
|
+
export * from './src/components/ui/checkbox'
|
|
49
|
+
export * from './src/components/ui/dialog'
|
|
50
|
+
export * from './src/components/ui/dropdown-menu'
|
|
51
|
+
export * from './src/components/ui/input'
|
|
52
|
+
export * from './src/components/ui/label'
|
|
53
|
+
export * from './src/components/ui/progress'
|
|
54
|
+
export * from './src/components/ui/select'
|
|
55
|
+
export * from './src/components/ui/separator'
|
|
56
|
+
export * from './src/components/ui/sonner'
|
|
57
|
+
export * from './src/components/ui/switch'
|
|
58
|
+
export * from './src/components/ui/table'
|
|
59
|
+
export * from './src/components/ui/tabs'
|
|
60
|
+
export * from './src/components/ui/textarea'
|
|
61
|
+
export * from './src/components/ui/tooltip'
|
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sviy/ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "House UI kit: Reka UI primitives styled with Tailwind CSS v4.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"typecheck": "vue-tsc --noEmit"
|
|
9
|
+
},
|
|
10
|
+
"sideEffects": [
|
|
11
|
+
"**/*.css"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"theme.css",
|
|
16
|
+
"index.ts",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./index.ts",
|
|
21
|
+
"./theme.css": "./theme.css",
|
|
22
|
+
"./lib/utils": "./src/lib/utils.ts",
|
|
23
|
+
"./components/*": "./src/components/*"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@fontsource/ibm-plex-sans": "^5.3.0",
|
|
27
|
+
"@vueuse/core": "^15.0.0",
|
|
28
|
+
"class-variance-authority": "^0.7.1",
|
|
29
|
+
"cn": "^0.5.1",
|
|
30
|
+
"reka-ui": "^2.11.0",
|
|
31
|
+
"tw-animate-css": "^1.4.0",
|
|
32
|
+
"vue-sonner": "^2.0.9"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@lucide/vue": "^1.66.0",
|
|
36
|
+
"tailwindcss": "^4.0.0",
|
|
37
|
+
"vue": "^3.5.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@lucide/vue": "^1.66.0",
|
|
41
|
+
"tailwindcss": "^4.3.3",
|
|
42
|
+
"typescript": "~6.0.3",
|
|
43
|
+
"vue": "^3.5.44",
|
|
44
|
+
"vue-tsc": "^3.3.13"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import type { AlertVariants } from '.'
|
|
4
|
+
import { cn } from 'cn'
|
|
5
|
+
import { alertVariants } from '.'
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{
|
|
8
|
+
class?: HTMLAttributes['class']
|
|
9
|
+
variant?: AlertVariants['variant']
|
|
10
|
+
}>()
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div
|
|
15
|
+
data-slot="alert"
|
|
16
|
+
:class="cn(alertVariants({ variant }), props.class)"
|
|
17
|
+
role="alert"
|
|
18
|
+
>
|
|
19
|
+
<slot />
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from 'cn'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div
|
|
12
|
+
data-slot="alert-action"
|
|
13
|
+
:class="cn('absolute top-2.5 right-3', props.class)"
|
|
14
|
+
>
|
|
15
|
+
<slot />
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from 'cn'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div
|
|
12
|
+
data-slot="alert-description"
|
|
13
|
+
:class="cn('text-muted-foreground text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground', props.class)"
|
|
14
|
+
>
|
|
15
|
+
<slot />
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from 'cn'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div
|
|
12
|
+
data-slot="alert-title"
|
|
13
|
+
:class="cn('font-medium group-has-[>svg]/alert:col-start-2 cn-font-heading [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground', props.class)"
|
|
14
|
+
>
|
|
15
|
+
<slot />
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
2
|
+
import { cva } from 'class-variance-authority'
|
|
3
|
+
|
|
4
|
+
export { default as Alert } from './Alert.vue'
|
|
5
|
+
export { default as AlertAction } from './AlertAction.vue'
|
|
6
|
+
export { default as AlertDescription } from './AlertDescription.vue'
|
|
7
|
+
export { default as AlertTitle } from './AlertTitle.vue'
|
|
8
|
+
|
|
9
|
+
export const alertVariants = cva('grid gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*=size-])]:size-4 group/alert relative w-full', {
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-card text-card-foreground',
|
|
13
|
+
destructive: 'text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
variant: 'default',
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export type AlertVariants = VariantProps<typeof alertVariants>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import type { AvatarVariants } from '.'
|
|
4
|
+
import { cn } from 'cn'
|
|
5
|
+
import { AvatarRoot } from 'reka-ui'
|
|
6
|
+
import { avatarVariants } from '.'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
class?: HTMLAttributes['class']
|
|
10
|
+
size?: AvatarVariants['size']
|
|
11
|
+
}>()
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<AvatarRoot
|
|
16
|
+
data-slot="avatar"
|
|
17
|
+
:data-size="size ?? 'default'"
|
|
18
|
+
:class="cn(avatarVariants({ size }), props.class)"
|
|
19
|
+
>
|
|
20
|
+
<slot />
|
|
21
|
+
</AvatarRoot>
|
|
22
|
+
</template>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from 'cn'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<span
|
|
12
|
+
data-slot="avatar-badge"
|
|
13
|
+
:class="cn(
|
|
14
|
+
'bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none',
|
|
15
|
+
'group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden',
|
|
16
|
+
'group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2',
|
|
17
|
+
'group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2',
|
|
18
|
+
props.class,
|
|
19
|
+
)"
|
|
20
|
+
>
|
|
21
|
+
<slot />
|
|
22
|
+
</span>
|
|
23
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { AvatarFallbackProps } from 'reka-ui'
|
|
3
|
+
import type { HTMLAttributes } from 'vue'
|
|
4
|
+
import { reactiveOmit } from '@vueuse/core'
|
|
5
|
+
import { cn } from 'cn'
|
|
6
|
+
import { AvatarFallback } from 'reka-ui'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<AvatarFallbackProps & { class?: HTMLAttributes['class'] }>()
|
|
9
|
+
|
|
10
|
+
const delegatedProps = reactiveOmit(props, 'class')
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<AvatarFallback
|
|
15
|
+
data-slot="avatar-fallback"
|
|
16
|
+
v-bind="delegatedProps"
|
|
17
|
+
:class="cn('bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs', props.class)"
|
|
18
|
+
>
|
|
19
|
+
<slot />
|
|
20
|
+
</AvatarFallback>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from 'cn'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div
|
|
12
|
+
data-slot="avatar-group"
|
|
13
|
+
:class="cn(
|
|
14
|
+
'group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background',
|
|
15
|
+
props.class,
|
|
16
|
+
)"
|
|
17
|
+
>
|
|
18
|
+
<slot />
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from 'cn'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div
|
|
12
|
+
data-slot="avatar-group-count"
|
|
13
|
+
:class="cn(
|
|
14
|
+
'bg-muted text-muted-foreground size-8 rounded-full text-sm group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 relative flex shrink-0 items-center justify-center ring-2 ring-background',
|
|
15
|
+
props.class,
|
|
16
|
+
)"
|
|
17
|
+
>
|
|
18
|
+
<slot />
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { AvatarImageProps } from 'reka-ui'
|
|
3
|
+
import { AvatarImage } from 'reka-ui'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<AvatarImageProps>()
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<AvatarImage
|
|
10
|
+
data-slot="avatar-image"
|
|
11
|
+
v-bind="props"
|
|
12
|
+
class="rounded-full aspect-square size-full object-cover"
|
|
13
|
+
>
|
|
14
|
+
<slot />
|
|
15
|
+
</AvatarImage>
|
|
16
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
2
|
+
import { cva } from 'class-variance-authority'
|
|
3
|
+
|
|
4
|
+
export { default as Avatar } from './Avatar.vue'
|
|
5
|
+
export { default as AvatarBadge } from './AvatarBadge.vue'
|
|
6
|
+
export { default as AvatarFallback } from './AvatarFallback.vue'
|
|
7
|
+
export { default as AvatarGroup } from './AvatarGroup.vue'
|
|
8
|
+
export { default as AvatarGroupCount } from './AvatarGroupCount.vue'
|
|
9
|
+
export { default as AvatarImage } from './AvatarImage.vue'
|
|
10
|
+
|
|
11
|
+
export const avatarVariants = cva(
|
|
12
|
+
'size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:border-border after:mix-blend-darken dark:after:mix-blend-lighten',
|
|
13
|
+
{
|
|
14
|
+
variants: {
|
|
15
|
+
size: {
|
|
16
|
+
sm: '',
|
|
17
|
+
default: '',
|
|
18
|
+
lg: '',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
size: 'default',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
export type AvatarVariants = VariantProps<typeof avatarVariants>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { PrimitiveProps } from 'reka-ui'
|
|
3
|
+
import type { HTMLAttributes } from 'vue'
|
|
4
|
+
import type { BadgeVariants } from '.'
|
|
5
|
+
import { reactiveOmit } from '@vueuse/core'
|
|
6
|
+
import { cn } from 'cn'
|
|
7
|
+
import { Primitive } from 'reka-ui'
|
|
8
|
+
import { badgeVariants } from '.'
|
|
9
|
+
|
|
10
|
+
const props = defineProps<PrimitiveProps & {
|
|
11
|
+
variant?: BadgeVariants['variant']
|
|
12
|
+
class?: HTMLAttributes['class']
|
|
13
|
+
}>()
|
|
14
|
+
|
|
15
|
+
const delegatedProps = reactiveOmit(props, 'class')
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<Primitive
|
|
20
|
+
data-slot="badge"
|
|
21
|
+
:data-variant="variant"
|
|
22
|
+
:class="cn(badgeVariants({ variant }), props.class)"
|
|
23
|
+
v-bind="delegatedProps"
|
|
24
|
+
>
|
|
25
|
+
<slot />
|
|
26
|
+
</Primitive>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
2
|
+
import { cva } from 'class-variance-authority'
|
|
3
|
+
|
|
4
|
+
export { default as Badge } from './Badge.vue'
|
|
5
|
+
|
|
6
|
+
export const badgeVariants = cva(
|
|
7
|
+
'h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! group/badge inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
|
|
12
|
+
secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
|
|
13
|
+
destructive: 'bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20',
|
|
14
|
+
outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
|
|
15
|
+
ghost: 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
|
|
16
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: {
|
|
20
|
+
variant: 'default',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
export type BadgeVariants = VariantProps<typeof badgeVariants>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { PrimitiveProps } from 'reka-ui'
|
|
3
|
+
import type { HTMLAttributes } from 'vue'
|
|
4
|
+
import type { ButtonVariants } from '.'
|
|
5
|
+
import { cn } from 'cn'
|
|
6
|
+
import { Primitive } from 'reka-ui'
|
|
7
|
+
import { buttonVariants } from '.'
|
|
8
|
+
|
|
9
|
+
interface Props extends PrimitiveProps {
|
|
10
|
+
variant?: ButtonVariants['variant']
|
|
11
|
+
size?: ButtonVariants['size']
|
|
12
|
+
class?: HTMLAttributes['class']
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
16
|
+
as: 'button',
|
|
17
|
+
})
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<Primitive
|
|
22
|
+
data-slot="button"
|
|
23
|
+
:data-variant="variant"
|
|
24
|
+
:data-size="size"
|
|
25
|
+
:as="as"
|
|
26
|
+
:as-child="asChild"
|
|
27
|
+
:class="cn(buttonVariants({ variant, size }), props.class)"
|
|
28
|
+
>
|
|
29
|
+
<slot />
|
|
30
|
+
</Primitive>
|
|
31
|
+
</template>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
2
|
+
import { cva } from 'class-variance-authority'
|
|
3
|
+
|
|
4
|
+
export { default as Button } from './Button.vue'
|
|
5
|
+
|
|
6
|
+
export const buttonVariants = cva(
|
|
7
|
+
'focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 aria-invalid:ring-3 active:not-aria-[haspopup]:translate-y-px [&_svg:not([class*=size-])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/80',
|
|
12
|
+
outline: 'border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground shadow-xs',
|
|
13
|
+
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
|
|
14
|
+
ghost: 'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground',
|
|
15
|
+
destructive: 'bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30',
|
|
16
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
'default': 'h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
|
|
20
|
+
'xs': 'h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*=size-])]:size-3',
|
|
21
|
+
'sm': 'h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5',
|
|
22
|
+
'lg': 'h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
|
|
23
|
+
'icon': 'size-9',
|
|
24
|
+
'icon-xs': 'size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*=size-])]:size-3',
|
|
25
|
+
'icon-sm': 'size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md',
|
|
26
|
+
'icon-lg': 'size-10',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: 'default',
|
|
31
|
+
size: 'default',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
)
|
|
35
|
+
export type ButtonVariants = VariantProps<typeof buttonVariants>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { cn } from 'cn'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
size?: 'default' | 'sm'
|
|
8
|
+
}>(), {
|
|
9
|
+
size: 'default',
|
|
10
|
+
})
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div
|
|
15
|
+
data-slot="card"
|
|
16
|
+
:data-size="size"
|
|
17
|
+
:class="cn('ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-xl py-6 text-sm shadow-xs ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col', props.class)"
|
|
18
|
+
>
|
|
19
|
+
<slot />
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|