@hemia/lume-registry 0.0.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 +43 -0
- package/package.json +14 -0
- package/registry/react/.gitkeep +0 -0
- package/registry/vue/alert/alert-action.vue +14 -0
- package/registry/vue/alert/alert-description.vue +14 -0
- package/registry/vue/alert/alert-title.vue +14 -0
- package/registry/vue/alert/alert.vue +42 -0
- package/registry/vue/alert/index.ts +4 -0
- package/registry/vue/alert/meta.json +19 -0
- package/registry/vue/alertdialog/alertdialog-action.vue +20 -0
- package/registry/vue/alertdialog/alertdialog-cancel.vue +32 -0
- package/registry/vue/alertdialog/alertdialog-content.vue +75 -0
- package/registry/vue/alertdialog/alertdialog-description.vue +13 -0
- package/registry/vue/alertdialog/alertdialog-footer.vue +34 -0
- package/registry/vue/alertdialog/alertdialog-header.vue +16 -0
- package/registry/vue/alertdialog/alertdialog-media.vue +13 -0
- package/registry/vue/alertdialog/alertdialog-title.vue +13 -0
- package/registry/vue/alertdialog/alertdialog-trigger.vue +29 -0
- package/registry/vue/alertdialog/alertdialog.vue +25 -0
- package/registry/vue/alertdialog/index.ts +10 -0
- package/registry/vue/alertdialog/meta.json +20 -0
- package/registry/vue/badge/badge.vue +39 -0
- package/registry/vue/badge/index.ts +1 -0
- package/registry/vue/badge/meta.json +16 -0
- package/registry/vue/button/button.vue +52 -0
- package/registry/vue/button/index.ts +1 -0
- package/registry/vue/button/meta.json +16 -0
- package/registry/vue/card/card-action.vue +21 -0
- package/registry/vue/card/card-content.vue +20 -0
- package/registry/vue/card/card-description.vue +13 -0
- package/registry/vue/card/card-footer.vue +24 -0
- package/registry/vue/card/card-header.vue +21 -0
- package/registry/vue/card/card-title.vue +13 -0
- package/registry/vue/card/card.vue +21 -0
- package/registry/vue/card/index.ts +7 -0
- package/registry/vue/card/meta.json +18 -0
- package/registry/vue/field/field-description.vue +16 -0
- package/registry/vue/field/field-group.vue +22 -0
- package/registry/vue/field/field-label.vue +19 -0
- package/registry/vue/field/field-legend.vue +16 -0
- package/registry/vue/field/field-separator.vue +14 -0
- package/registry/vue/field/field.vue +29 -0
- package/registry/vue/field/fieldset.vue +13 -0
- package/registry/vue/field/index.ts +7 -0
- package/registry/vue/field/meta.json +22 -0
- package/registry/vue/icon/icon.vue +37 -0
- package/registry/vue/icon/index.ts +2 -0
- package/registry/vue/icon/meta.json +16 -0
- package/registry/vue/textfield/index.ts +1 -0
- package/registry/vue/textfield/meta.json +16 -0
- package/registry/vue/textfield/textfield.vue +157 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @hemia/lume-registry
|
|
2
|
+
|
|
3
|
+
Component templates registry for the Lume UI system. Contains source code for all Lume components organized by framework.
|
|
4
|
+
|
|
5
|
+
## What is this?
|
|
6
|
+
|
|
7
|
+
This package stores the component templates that the Lume CLI copies into user projects. **Users don't install this directly** — it's consumed by the `@hemia/lume` CLI.
|
|
8
|
+
|
|
9
|
+
## Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
registry/
|
|
13
|
+
├── vue/
|
|
14
|
+
│ ├── button/
|
|
15
|
+
│ │ ├── button.vue
|
|
16
|
+
│ │ └── meta.json
|
|
17
|
+
│ └── card/
|
|
18
|
+
│ ├── card.vue
|
|
19
|
+
│ └── meta.json
|
|
20
|
+
└── react/ (coming soon)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## How it works
|
|
24
|
+
|
|
25
|
+
When a user runs:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bunx @hemia/lume@latest add button
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The CLI:
|
|
32
|
+
1. Reads the framework from `lume.config.json`
|
|
33
|
+
2. Resolves `@hemia/lume-registry` package location
|
|
34
|
+
3. Copies files from `registry/{framework}/{component}/` to the user's project
|
|
35
|
+
4. Installs dependencies listed in `meta.json`
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
This package is dependency of `@hemia/lume` CLI. It's not meant to be imported in user code.
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hemia/lume-registry",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": ["registry"],
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"@hemia/lume-vue": "workspace:*",
|
|
11
|
+
"class-variance-authority": "^0.7.1",
|
|
12
|
+
"vue": "^3.4.0"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- eslint-disable vue/no-parsing-error -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: string
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div :class="cn('flex items-center gap-2', props.class)">
|
|
12
|
+
<slot />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- eslint-disable vue/no-parsing-error -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: string
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div :class="cn('text-sm [&_p]:leading-relaxed', props.class)">
|
|
12
|
+
<slot />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- eslint-disable vue/no-parsing-error -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: string
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<h5 :class="cn('mb-1 font-medium leading-none tracking-tight', props.class)">
|
|
12
|
+
<slot />
|
|
13
|
+
</h5>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!-- eslint-disable vue/no-parsing-error -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
import { cn } from "@hemia/lume-vue"
|
|
5
|
+
|
|
6
|
+
const alertVariants = cva(
|
|
7
|
+
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-muted/50 text-foreground border-border",
|
|
12
|
+
destructive:
|
|
13
|
+
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive bg-destructive/10 dark:bg-destructive/20",
|
|
14
|
+
tonal:
|
|
15
|
+
"border-transparent bg-destructive/10 text-destructive dark:bg-destructive/30 dark:text-destructive-200",
|
|
16
|
+
success:
|
|
17
|
+
"border-transparent bg-emerald-500/10 text-emerald-700 dark:text-emerald-200 dark:bg-emerald-500/30",
|
|
18
|
+
warning:
|
|
19
|
+
"border-transparent bg-amber-500/10 text-amber-700 dark:text-amber-200 dark:bg-amber-500/30",
|
|
20
|
+
info:
|
|
21
|
+
"border-transparent bg-blue-500/10 text-blue-700 dark:text-blue-200 dark:bg-blue-500/30",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "default",
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
type AlertVariants = VariantProps<typeof alertVariants>
|
|
31
|
+
|
|
32
|
+
const props = defineProps<{
|
|
33
|
+
variant?: AlertVariants["variant"]
|
|
34
|
+
class?: string
|
|
35
|
+
}>()
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div :class="cn(alertVariants({ variant }), props.class)" role="alert">
|
|
40
|
+
<slot />
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "alert",
|
|
3
|
+
"framework": "vue",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"files": [
|
|
6
|
+
"alert.vue",
|
|
7
|
+
"alert-title.vue",
|
|
8
|
+
"alert-description.vue",
|
|
9
|
+
"alert-action.vue",
|
|
10
|
+
"index.ts"
|
|
11
|
+
],
|
|
12
|
+
"registryDependencies": [],
|
|
13
|
+
"dependencies": [
|
|
14
|
+
"class-variance-authority"
|
|
15
|
+
],
|
|
16
|
+
"peerDependencies": [
|
|
17
|
+
"@hemia/lume-vue"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, computed } from 'vue'
|
|
3
|
+
import { cn } from '@hemia/lume-vue'
|
|
4
|
+
import { Button } from '../button'
|
|
5
|
+
|
|
6
|
+
const size = inject<string>('alert-dialog-size', 'md')
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
variant?: 'default' | 'destructive'
|
|
10
|
+
class?: string
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
const isSm = computed(() => size === 'sm')
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<Button :variant="props.variant" :class="cn(isSm && 'w-full justify-center', props.class)">
|
|
18
|
+
<slot />
|
|
19
|
+
</Button>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, type Ref, computed } from 'vue'
|
|
3
|
+
import { cn } from '@hemia/lume-vue'
|
|
4
|
+
import { Button } from '../button'
|
|
5
|
+
|
|
6
|
+
const dialog = inject<{
|
|
7
|
+
open: Ref<boolean>
|
|
8
|
+
onOpenChange: (value: boolean) => void
|
|
9
|
+
}>('alert-dialog')
|
|
10
|
+
|
|
11
|
+
const size = inject<string>('alert-dialog-size', 'md')
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
class?: string
|
|
15
|
+
}>()
|
|
16
|
+
|
|
17
|
+
function close() {
|
|
18
|
+
dialog?.onOpenChange(false)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const isSm = computed(() => size === 'sm')
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<Button
|
|
26
|
+
variant="outline"
|
|
27
|
+
:class="cn(isSm && 'w-full justify-center', props.class)"
|
|
28
|
+
@click="close"
|
|
29
|
+
>
|
|
30
|
+
<slot />
|
|
31
|
+
</Button>
|
|
32
|
+
</template>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, type Ref, computed, provide } from 'vue'
|
|
3
|
+
import { cn } from '@hemia/lume-vue'
|
|
4
|
+
|
|
5
|
+
const dialog = inject<{
|
|
6
|
+
open: Ref<boolean>
|
|
7
|
+
onOpenChange: (value: boolean) => void
|
|
8
|
+
}>('alert-dialog')
|
|
9
|
+
|
|
10
|
+
const props = defineProps<{
|
|
11
|
+
class?: string
|
|
12
|
+
size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'
|
|
13
|
+
persistent?: boolean
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
// Provide size to child components
|
|
17
|
+
provide('alert-dialog-size', props.size || 'md')
|
|
18
|
+
|
|
19
|
+
function close() {
|
|
20
|
+
dialog?.onOpenChange(false)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const isOpen = computed(() => dialog?.open.value ?? false)
|
|
24
|
+
|
|
25
|
+
const sizeClasses = computed(() => {
|
|
26
|
+
const sizes = {
|
|
27
|
+
sm: 'max-w-sm',
|
|
28
|
+
md: 'max-w-lg',
|
|
29
|
+
lg: 'max-w-2xl',
|
|
30
|
+
xl: 'max-w-4xl',
|
|
31
|
+
full: 'max-w-[calc(100vw-2rem)] h-[calc(100vh-2rem)]'
|
|
32
|
+
}
|
|
33
|
+
return sizes[props.size || 'md']
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const isCentered = computed(() => props.size === 'sm')
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<Teleport to="body">
|
|
41
|
+
<Transition
|
|
42
|
+
enter-active-class="transition-all duration-200 ease-out"
|
|
43
|
+
enter-from-class="opacity-0"
|
|
44
|
+
enter-to-class="opacity-100"
|
|
45
|
+
leave-active-class="transition-all duration-150 ease-in"
|
|
46
|
+
leave-from-class="opacity-100"
|
|
47
|
+
leave-to-class="opacity-0"
|
|
48
|
+
>
|
|
49
|
+
<div
|
|
50
|
+
v-if="isOpen"
|
|
51
|
+
class="fixed inset-0 z-50 bg-black/80"
|
|
52
|
+
:class="{ 'cursor-not-allowed pointer-events-none': props.persistent }"
|
|
53
|
+
@click="!props.persistent && close()"
|
|
54
|
+
/>
|
|
55
|
+
</Transition>
|
|
56
|
+
</Teleport>
|
|
57
|
+
<Teleport to="body">
|
|
58
|
+
<Transition
|
|
59
|
+
enter-active-class="transition-all duration-200 ease-out"
|
|
60
|
+
enter-from-class="opacity-0 scale-95 translate-y-4"
|
|
61
|
+
enter-to-class="opacity-100 scale-100 translate-y-0"
|
|
62
|
+
leave-active-class="transition-all duration-150 ease-in"
|
|
63
|
+
leave-from-class="opacity-100 scale-100 translate-y-0"
|
|
64
|
+
leave-to-class="opacity-0 scale-95 translate-y-4"
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
v-if="isOpen"
|
|
68
|
+
class="fixed left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%] gap-0 border bg-background shadow-lg duration-200 sm:rounded-lg"
|
|
69
|
+
:class="cn(sizeClasses, isCentered && 'text-center', props.class)"
|
|
70
|
+
>
|
|
71
|
+
<slot />
|
|
72
|
+
</div>
|
|
73
|
+
</Transition>
|
|
74
|
+
</Teleport>
|
|
75
|
+
</template>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, type Ref, computed } from 'vue'
|
|
3
|
+
import { cn } from '@hemia/lume-vue'
|
|
4
|
+
|
|
5
|
+
const dialog = inject<{
|
|
6
|
+
open: Ref<boolean>
|
|
7
|
+
}>('alert-dialog')
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
class?: string
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
const isOpen = computed(() => dialog?.open.value ?? false)
|
|
14
|
+
|
|
15
|
+
// Obtener el tamaño del content a través del provide/inject
|
|
16
|
+
const size = inject<string>('alert-dialog-size', 'md')
|
|
17
|
+
|
|
18
|
+
const isSm = computed(() => size === 'sm')
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<div
|
|
23
|
+
:class="cn(
|
|
24
|
+
isSm
|
|
25
|
+
? 'flex-col-reverse'
|
|
26
|
+
: 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
|
|
27
|
+
'bg-muted/50 rounded-b-lg px-6 py-4 dark:bg-muted/30',
|
|
28
|
+
isSm && 'grid grid-cols-2 gap-2',
|
|
29
|
+
props.class
|
|
30
|
+
)"
|
|
31
|
+
>
|
|
32
|
+
<slot />
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cn } from '@hemia/lume-vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
class?: string
|
|
6
|
+
}>()
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div :class="cn('flex flex-col space-y-1.5 text-center sm:text-left px-6 pt-6 pb-2', props.class)">
|
|
11
|
+
<div v-if="$slots.media" class="flex justify-center">
|
|
12
|
+
<slot name="media" />
|
|
13
|
+
</div>
|
|
14
|
+
<slot />
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cn } from '@hemia/lume-vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
class?: string
|
|
6
|
+
}>()
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div :class="cn('w-auto inline-flex items-center justify-center rounded-md p-3', props.class)">
|
|
11
|
+
<slot />
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cn } from '@hemia/lume-vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
class?: string
|
|
6
|
+
}>()
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<h2 :class="cn('text-lg font-semibold leading-none tracking-tight', props.class)">
|
|
11
|
+
<slot />
|
|
12
|
+
</h2>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, type Ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
const dialog = inject<{
|
|
5
|
+
open: Ref<boolean>
|
|
6
|
+
onOpenChange: (value: boolean) => void
|
|
7
|
+
}>('alert-dialog')
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
asChild?: boolean
|
|
11
|
+
class?: string
|
|
12
|
+
}>()
|
|
13
|
+
|
|
14
|
+
function onClick(event: MouseEvent) {
|
|
15
|
+
// Stop propagation to prevent double-firing
|
|
16
|
+
event.stopPropagation()
|
|
17
|
+
dialog?.onOpenChange(true)
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<component
|
|
23
|
+
:is="props.asChild ? 'span' : 'button'"
|
|
24
|
+
:class="props.class"
|
|
25
|
+
@click="onClick"
|
|
26
|
+
>
|
|
27
|
+
<slot />
|
|
28
|
+
</component>
|
|
29
|
+
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cn } from '@hemia/lume-vue'
|
|
3
|
+
import { ref, provide } from 'vue'
|
|
4
|
+
|
|
5
|
+
const open = ref(false)
|
|
6
|
+
|
|
7
|
+
function onOpenChange(value: boolean) {
|
|
8
|
+
open.value = value
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
provide('alert-dialog', {
|
|
12
|
+
open,
|
|
13
|
+
onOpenChange,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const props = defineProps<{
|
|
17
|
+
class?: string
|
|
18
|
+
}>()
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<div :class="cn(props.class)">
|
|
23
|
+
<slot :open="open" :on-open-change="onOpenChange" />
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { default as AlertDialog } from "./alertdialog.vue"
|
|
2
|
+
export { default as AlertDialogTrigger } from "./alertdialog-trigger.vue"
|
|
3
|
+
export { default as AlertDialogContent } from "./alertdialog-content.vue"
|
|
4
|
+
export { default as AlertDialogHeader } from "./alertdialog-header.vue"
|
|
5
|
+
export { default as AlertDialogFooter } from "./alertdialog-footer.vue"
|
|
6
|
+
export { default as AlertDialogTitle } from "./alertdialog-title.vue"
|
|
7
|
+
export { default as AlertDialogDescription } from "./alertdialog-description.vue"
|
|
8
|
+
export { default as AlertDialogCancel } from "./alertdialog-cancel.vue"
|
|
9
|
+
export { default as AlertDialogAction } from "./alertdialog-action.vue"
|
|
10
|
+
export { default as AlertDialogMedia } from "./alertdialog-media.vue"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "alert-dialog",
|
|
3
|
+
"framework": "vue",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"files": [
|
|
6
|
+
"alertdialog.vue",
|
|
7
|
+
"alertdialog-trigger.vue",
|
|
8
|
+
"alertdialog-content.vue",
|
|
9
|
+
"alertdialog-header.vue",
|
|
10
|
+
"alertdialog-footer.vue",
|
|
11
|
+
"alertdialog-title.vue",
|
|
12
|
+
"alertdialog-description.vue",
|
|
13
|
+
"alertdialog-cancel.vue",
|
|
14
|
+
"alertdialog-action.vue",
|
|
15
|
+
"index.ts"
|
|
16
|
+
],
|
|
17
|
+
"registryDependencies": ["button"],
|
|
18
|
+
"dependencies": [],
|
|
19
|
+
"peerDependencies": ["@hemia/lume-vue"]
|
|
20
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const badgeVariants = cva(
|
|
6
|
+
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default:
|
|
11
|
+
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
|
12
|
+
secondary:
|
|
13
|
+
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
14
|
+
destructive:
|
|
15
|
+
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
|
16
|
+
outline: "text-foreground",
|
|
17
|
+
ghost: "border-transparent hover:bg-accent hover:text-accent-foreground",
|
|
18
|
+
link: "border-transparent text-primary underline-offset-4 hover:underline",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
variant: "default",
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
type BadgeVariants = VariantProps<typeof badgeVariants>
|
|
28
|
+
|
|
29
|
+
defineProps<{
|
|
30
|
+
variant?: BadgeVariants["variant"]
|
|
31
|
+
class?: string
|
|
32
|
+
}>()
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<div :class="cn(badgeVariants({ variant }), $props.class)">
|
|
37
|
+
<slot />
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Badge } from "./badge.vue"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "badge",
|
|
3
|
+
"framework": "vue",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"files": [
|
|
6
|
+
"badge.vue",
|
|
7
|
+
"index.ts"
|
|
8
|
+
],
|
|
9
|
+
"registryDependencies": [],
|
|
10
|
+
"dependencies": [
|
|
11
|
+
"class-variance-authority"
|
|
12
|
+
],
|
|
13
|
+
"peerDependencies": [
|
|
14
|
+
"@hemia/lume-vue"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const buttonVariants = cva(
|
|
6
|
+
"group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded-[calc(var(--radius)*2.6)] border border-transparent bg-clip-padding text-sm font-medium transition-all outline-none select-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
11
|
+
destructive:
|
|
12
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
13
|
+
outline:
|
|
14
|
+
"border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
15
|
+
secondary:
|
|
16
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
17
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
18
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
default: "h-9 px-4 py-2",
|
|
22
|
+
xs: "h-7 rounded-md px-2 text-xs [&_svg:not([class*='size-'])]:size-3",
|
|
23
|
+
sm: "h-8 rounded-md px-3",
|
|
24
|
+
lg: "h-10 rounded-md px-8",
|
|
25
|
+
icon: "size-9",
|
|
26
|
+
"icon-xs": "size-7 [&_svg:not([class*='size-'])]:size-3",
|
|
27
|
+
"icon-sm": "size-8",
|
|
28
|
+
"icon-lg": "size-10",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: {
|
|
32
|
+
variant: "default",
|
|
33
|
+
size: "default",
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
type ButtonVariants = VariantProps<typeof buttonVariants>
|
|
39
|
+
|
|
40
|
+
const props = defineProps<{
|
|
41
|
+
variant?: ButtonVariants["variant"]
|
|
42
|
+
size?: ButtonVariants["size"]
|
|
43
|
+
type?: "button" | "submit" | "reset"
|
|
44
|
+
class?: string
|
|
45
|
+
}>()
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template>
|
|
49
|
+
<button :type="props.type" :class="cn(buttonVariants({ variant, size }), props.class)">
|
|
50
|
+
<slot />
|
|
51
|
+
</button>
|
|
52
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Button } from "./button.vue"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "button",
|
|
3
|
+
"framework": "vue",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"files": [
|
|
6
|
+
"button.vue",
|
|
7
|
+
"index.ts"
|
|
8
|
+
],
|
|
9
|
+
"registryDependencies": [],
|
|
10
|
+
"dependencies": [
|
|
11
|
+
"class-variance-authority"
|
|
12
|
+
],
|
|
13
|
+
"peerDependencies": [
|
|
14
|
+
"@hemia/lume-vue"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, computed } from 'vue'
|
|
3
|
+
import { cn } from '@hemia/lume-vue'
|
|
4
|
+
|
|
5
|
+
const size = inject<string>('card-size', 'default')
|
|
6
|
+
const isSm = computed(() => size === 'sm')
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
class?: string
|
|
10
|
+
}>()
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div :class="cn(
|
|
15
|
+
'absolute right-4 top-4 flex items-center gap-2',
|
|
16
|
+
isSm ? 'right-2 top-2' : '',
|
|
17
|
+
props.class
|
|
18
|
+
)">
|
|
19
|
+
<slot />
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, computed } from 'vue'
|
|
3
|
+
import { cn } from '@hemia/lume-vue'
|
|
4
|
+
|
|
5
|
+
const size = inject<string>('card-size', 'default')
|
|
6
|
+
const isSm = computed(() => size === 'sm')
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
class?: string
|
|
10
|
+
}>()
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div :class="cn(
|
|
15
|
+
isSm ? 'pt-3 pb-3' : 'pt-6 pb-6',
|
|
16
|
+
props.class
|
|
17
|
+
)">
|
|
18
|
+
<slot />
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, computed } from 'vue'
|
|
3
|
+
import { cn } from '@hemia/lume-vue'
|
|
4
|
+
|
|
5
|
+
const size = inject<string>('card-size', 'default')
|
|
6
|
+
|
|
7
|
+
const isSm = computed(() => size === 'sm')
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
class?: string
|
|
11
|
+
}>()
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<div :class="cn(
|
|
16
|
+
isSm
|
|
17
|
+
? 'flex-col-reverse w-[calc(100%+1rem)] -mx-2 -mb-2 px-2'
|
|
18
|
+
: 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 -mx-6 -mb-6 px-6',
|
|
19
|
+
'bg-muted/50 rounded-b-xl dark:bg-muted/30 py-3',
|
|
20
|
+
props.class
|
|
21
|
+
)">
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, computed } from 'vue'
|
|
3
|
+
import { cn } from '@hemia/lume-vue'
|
|
4
|
+
|
|
5
|
+
const size = inject<string>('card-size', 'default')
|
|
6
|
+
const isSm = computed(() => size === 'sm')
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
class?: string
|
|
10
|
+
}>()
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div :class="cn(
|
|
15
|
+
'flex flex-col',
|
|
16
|
+
isSm ? 'space-y-2 pr-6 mb-2' : 'space-y-1.5',
|
|
17
|
+
props.class
|
|
18
|
+
)">
|
|
19
|
+
<slot />
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cn } from '@hemia/lume-vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
class?: string
|
|
6
|
+
}>()
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<h3 :class="cn('font-semibold leading-none tracking-tight', props.class)">
|
|
11
|
+
<slot />
|
|
12
|
+
</h3>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { provide } from 'vue'
|
|
3
|
+
import { cn } from '@hemia/lume-vue'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
size?: 'default' | 'sm'
|
|
7
|
+
class?: string
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
provide('card-size', props.size || 'default')
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div :class="cn(
|
|
15
|
+
'relative rounded-xl border bg-card text-card-foreground shadow',
|
|
16
|
+
size === 'sm' ? 'p-2 pb-2' : 'p-6',
|
|
17
|
+
props.class
|
|
18
|
+
)">
|
|
19
|
+
<slot />
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as Card } from "./card.vue"
|
|
2
|
+
export { default as CardHeader } from "./card-header.vue"
|
|
3
|
+
export { default as CardTitle } from "./card-title.vue"
|
|
4
|
+
export { default as CardDescription } from "./card-description.vue"
|
|
5
|
+
export { default as CardContent } from "./card-content.vue"
|
|
6
|
+
export { default as CardFooter } from "./card-footer.vue"
|
|
7
|
+
export { default as CardAction } from "./card-action.vue"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "card",
|
|
3
|
+
"framework": "vue",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"files": [
|
|
6
|
+
"card.vue",
|
|
7
|
+
"card-header.vue",
|
|
8
|
+
"card-title.vue",
|
|
9
|
+
"card-description.vue",
|
|
10
|
+
"card-content.vue",
|
|
11
|
+
"card-footer.vue",
|
|
12
|
+
"card-action.vue",
|
|
13
|
+
"index.ts"
|
|
14
|
+
],
|
|
15
|
+
"registryDependencies": [],
|
|
16
|
+
"dependencies": [],
|
|
17
|
+
"peerDependencies": ["@hemia/lume-vue"]
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const fieldDescriptionVariants = cva("text-[0.8rem] text-muted-foreground")
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{
|
|
8
|
+
class?: string
|
|
9
|
+
}>()
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<p :class="cn(fieldDescriptionVariants(), props.class)">
|
|
14
|
+
<slot />
|
|
15
|
+
</p>
|
|
16
|
+
</template>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const fieldGroupVariants = cva(
|
|
6
|
+
"flex flex-wrap items-center gap-2 has-[input:focus]:rounded-lg has-[input:focus]:border-ring has-[input:focus]:ring-3 has-[input:focus]:ring-ring/50"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
class?: string
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
defineOptions({
|
|
14
|
+
inheritAttrs: false
|
|
15
|
+
})
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<div :class="cn(fieldGroupVariants(), props.class)" v-bind="$attrs">
|
|
20
|
+
<slot />
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const fieldLabelVariants = cva(
|
|
6
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
forId?: string
|
|
11
|
+
class?: string
|
|
12
|
+
}>()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<label :for="forId" :class="cn(fieldLabelVariants(), props.class)">
|
|
17
|
+
<slot />
|
|
18
|
+
</label>
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const fieldLegendVariants = cva("text-sm font-medium leading-none")
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{
|
|
8
|
+
class?: string
|
|
9
|
+
}>()
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<legend :class="cn(fieldLegendVariants(), props.class)">
|
|
14
|
+
<slot />
|
|
15
|
+
</legend>
|
|
16
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const fieldSeparatorVariants = cva("h-px bg-border")
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{
|
|
8
|
+
class?: string
|
|
9
|
+
}>()
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div :class="cn(fieldSeparatorVariants(), props.class)" />
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const fieldVariants = cva("grid gap-1.5 rounded-lg", {
|
|
6
|
+
variants: {
|
|
7
|
+
orientation: {
|
|
8
|
+
horizontal: "grid-cols-[1fr,auto]",
|
|
9
|
+
vertical: "",
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
defaultVariants: {
|
|
13
|
+
orientation: "vertical",
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
type FieldVariants = VariantProps<typeof fieldVariants>
|
|
18
|
+
|
|
19
|
+
const props = defineProps<{
|
|
20
|
+
orientation?: FieldVariants["orientation"]
|
|
21
|
+
class?: string
|
|
22
|
+
}>()
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<div :class="cn(fieldVariants({ orientation }), props.class)">
|
|
27
|
+
<slot />
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as Field } from "./field.vue"
|
|
2
|
+
export { default as FieldLabel } from "./field-label.vue"
|
|
3
|
+
export { default as FieldDescription } from "./field-description.vue"
|
|
4
|
+
export { default as FieldGroup } from "./field-group.vue"
|
|
5
|
+
export { default as FieldLegend } from "./field-legend.vue"
|
|
6
|
+
export { default as FieldSeparator } from "./field-separator.vue"
|
|
7
|
+
export { default as FieldSet } from "./fieldset.vue"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "field",
|
|
3
|
+
"framework": "vue",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"files": [
|
|
6
|
+
"field.vue",
|
|
7
|
+
"field-label.vue",
|
|
8
|
+
"field-description.vue",
|
|
9
|
+
"field-group.vue",
|
|
10
|
+
"field-legend.vue",
|
|
11
|
+
"field-separator.vue",
|
|
12
|
+
"fieldset.vue",
|
|
13
|
+
"index.ts"
|
|
14
|
+
],
|
|
15
|
+
"registryDependencies": [],
|
|
16
|
+
"dependencies": [
|
|
17
|
+
"class-variance-authority"
|
|
18
|
+
],
|
|
19
|
+
"peerDependencies": [
|
|
20
|
+
"@hemia/lume-vue"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
|
|
5
|
+
const iconVariants = cva(
|
|
6
|
+
"inline-flex shrink-0 items-center justify-center",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
size: {
|
|
10
|
+
default: "size-4",
|
|
11
|
+
sm: "size-3",
|
|
12
|
+
lg: "size-5",
|
|
13
|
+
xl: "size-6",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
size: "default",
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
type IconVariants = VariantProps<typeof iconVariants>
|
|
23
|
+
|
|
24
|
+
// Export for external use
|
|
25
|
+
defineExpose({ iconVariants, IconVariants })
|
|
26
|
+
|
|
27
|
+
defineProps<{
|
|
28
|
+
size?: IconVariants["size"]
|
|
29
|
+
class?: string
|
|
30
|
+
}>()
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<span :class="cn(iconVariants({ size }), props.class)">
|
|
35
|
+
<slot />
|
|
36
|
+
</span>
|
|
37
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "icon",
|
|
3
|
+
"framework": "vue",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"files": [
|
|
6
|
+
"icon.vue",
|
|
7
|
+
"index.ts"
|
|
8
|
+
],
|
|
9
|
+
"registryDependencies": [],
|
|
10
|
+
"dependencies": [
|
|
11
|
+
"class-variance-authority"
|
|
12
|
+
],
|
|
13
|
+
"peerDependencies": [
|
|
14
|
+
"@hemia/lume-vue"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as TextField } from "./textfield.vue"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "textfield",
|
|
3
|
+
"framework": "vue",
|
|
4
|
+
"type": "component",
|
|
5
|
+
"files": [
|
|
6
|
+
"textfield.vue",
|
|
7
|
+
"index.ts"
|
|
8
|
+
],
|
|
9
|
+
"registryDependencies": ["field"],
|
|
10
|
+
"dependencies": [
|
|
11
|
+
"class-variance-authority"
|
|
12
|
+
],
|
|
13
|
+
"peerDependencies": [
|
|
14
|
+
"@hemia/lume-vue"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@hemia/lume-vue"
|
|
4
|
+
import { Field, FieldLabel, FieldDescription, FieldGroup } from "../field/index"
|
|
5
|
+
|
|
6
|
+
const textfieldVariants = cva(
|
|
7
|
+
"flex h-9 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "",
|
|
12
|
+
error: "bg-background border-destructive aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
13
|
+
},
|
|
14
|
+
size: {
|
|
15
|
+
default: "",
|
|
16
|
+
sm: "h-8",
|
|
17
|
+
lg: "h-10",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: {
|
|
21
|
+
variant: "default",
|
|
22
|
+
size: "default",
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const textfieldIconVariants = cva(
|
|
28
|
+
"text-muted-foreground flex shrink-0 items-center",
|
|
29
|
+
{
|
|
30
|
+
variants: {
|
|
31
|
+
size: {
|
|
32
|
+
default: "size-4",
|
|
33
|
+
sm: "size-3.5",
|
|
34
|
+
lg: "size-5",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
size: "default",
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
type TextfieldVariants = VariantProps<typeof textfieldVariants>
|
|
44
|
+
type TextfieldIconVariants = VariantProps<typeof textfieldIconVariants>
|
|
45
|
+
|
|
46
|
+
const props = defineProps<{
|
|
47
|
+
label?: string
|
|
48
|
+
description?: string
|
|
49
|
+
error?: string
|
|
50
|
+
forId?: string
|
|
51
|
+
variant?: TextfieldVariants["variant"]
|
|
52
|
+
size?: TextfieldVariants["size"]
|
|
53
|
+
iconSize?: TextfieldIconVariants["size"]
|
|
54
|
+
modelValue?: string
|
|
55
|
+
type?: string
|
|
56
|
+
placeholder?: string
|
|
57
|
+
disabled?: boolean
|
|
58
|
+
class?: string
|
|
59
|
+
prependIcon?: any
|
|
60
|
+
prependInnerIcon?: any
|
|
61
|
+
appendInnerIcon?: any
|
|
62
|
+
appendIcon?: any
|
|
63
|
+
}>()
|
|
64
|
+
|
|
65
|
+
const emit = defineEmits<{
|
|
66
|
+
(e: "update:modelValue", value: string): void
|
|
67
|
+
}>()
|
|
68
|
+
|
|
69
|
+
function onInput(event: Event) {
|
|
70
|
+
const target = event.target as HTMLInputElement
|
|
71
|
+
emit("update:modelValue", target.value)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Compute padding classes for input based on inner icon presence
|
|
75
|
+
const getInputPaddingClass = () => {
|
|
76
|
+
const padding = []
|
|
77
|
+
if (props.prependInnerIcon) {
|
|
78
|
+
padding.push("ps-1.5")
|
|
79
|
+
} else {
|
|
80
|
+
padding.push("ps-[var(--tf-padding-x)]")
|
|
81
|
+
}
|
|
82
|
+
if (props.appendInnerIcon) {
|
|
83
|
+
padding.push("pe-1.5")
|
|
84
|
+
} else {
|
|
85
|
+
padding.push("pe-[var(--tf-padding-x)]")
|
|
86
|
+
}
|
|
87
|
+
return padding.join(" ")
|
|
88
|
+
}
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<template>
|
|
92
|
+
<Field :class="props.class">
|
|
93
|
+
<FieldLabel v-if="label" :for="forId">{{ label }}</FieldLabel>
|
|
94
|
+
<FieldGroup class="flex flex-1 items-center">
|
|
95
|
+
<!-- Prepend icon (outside the textfield, to the left) -->
|
|
96
|
+
<component
|
|
97
|
+
v-if="prependIcon"
|
|
98
|
+
:is="prependIcon"
|
|
99
|
+
class="text-muted-foreground flex shrink-0 items-center"
|
|
100
|
+
:class="iconSize === 'sm' ? 'size-3.5' : iconSize === 'lg' ? 'size-5' : 'size-4'"
|
|
101
|
+
/>
|
|
102
|
+
<!-- Textfield inner container -->
|
|
103
|
+
<div
|
|
104
|
+
:class="cn(
|
|
105
|
+
textfieldVariants({ variant: error ? 'error' : variant, size }),
|
|
106
|
+
'flex flex-1 items-center focus-within:rounded-lg focus-within:ring-2 focus-within:ring-ring/50 dark:focus-within:[--ring:217.2_32.6%_50%] focus-within:[--ring:210_5%_80%]'
|
|
107
|
+
)"
|
|
108
|
+
>
|
|
109
|
+
<!-- Prepend-inner icon (inside, at start) -->
|
|
110
|
+
<component
|
|
111
|
+
v-if="prependInnerIcon"
|
|
112
|
+
:is="prependInnerIcon"
|
|
113
|
+
class="text-muted-foreground flex shrink-0 items-center"
|
|
114
|
+
:class="[
|
|
115
|
+
iconSize === 'sm' ? 'size-3.5' : iconSize === 'lg' ? 'size-5' : 'size-4',
|
|
116
|
+
'me-1.5'
|
|
117
|
+
]"
|
|
118
|
+
/>
|
|
119
|
+
<input
|
|
120
|
+
:id="forId"
|
|
121
|
+
:type="type"
|
|
122
|
+
:value="modelValue"
|
|
123
|
+
:placeholder="placeholder"
|
|
124
|
+
:disabled="disabled"
|
|
125
|
+
:aria-invalid="!!error"
|
|
126
|
+
:class="cn(
|
|
127
|
+
'flex-1 bg-transparent outline-none placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 w-full min-w-0',
|
|
128
|
+
getInputPaddingClass()
|
|
129
|
+
)"
|
|
130
|
+
:style="{ '--tf-padding-x': '0.625rem' }"
|
|
131
|
+
@input="onInput"
|
|
132
|
+
/>
|
|
133
|
+
<!-- Append-inner icon (inside, at end) -->
|
|
134
|
+
<component
|
|
135
|
+
v-if="appendInnerIcon"
|
|
136
|
+
:is="appendInnerIcon"
|
|
137
|
+
class="text-muted-foreground flex shrink-0 items-center"
|
|
138
|
+
:class="[
|
|
139
|
+
iconSize === 'sm' ? 'size-3.5' : iconSize === 'lg' ? 'size-5' : 'size-4',
|
|
140
|
+
'ms-1.5'
|
|
141
|
+
]"
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
<!-- Append icon (outside the textfield, to the right) -->
|
|
145
|
+
<component
|
|
146
|
+
v-if="appendIcon"
|
|
147
|
+
:is="appendIcon"
|
|
148
|
+
class="text-muted-foreground flex shrink-0 items-center"
|
|
149
|
+
:class="iconSize === 'sm' ? 'size-3.5' : iconSize === 'lg' ? 'size-5' : 'size-4'"
|
|
150
|
+
/>
|
|
151
|
+
</FieldGroup>
|
|
152
|
+
<FieldDescription v-if="description && !error">{{ description }}</FieldDescription>
|
|
153
|
+
<p v-if="error" class="text-[0.8rem] font-medium text-destructive">
|
|
154
|
+
{{ error }}
|
|
155
|
+
</p>
|
|
156
|
+
</Field>
|
|
157
|
+
</template>
|