@brickflow/ui 0.0.57 → 0.0.58
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/dist/module.d.mts +3 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +33 -7
- package/dist/runtime/brickflow-ui.d.ts +1 -0
- package/dist/runtime/components/Button/index.vue +5 -3
- package/dist/runtime/components/Dialog/DemoContent.vue +33 -0
- package/dist/runtime/components/Dialog/DemoForm.vue +54 -0
- package/dist/runtime/components/Dialog/DemoSimple.vue +23 -0
- package/dist/runtime/components/Dialog/DemoStack.vue +41 -0
- package/dist/runtime/components/Dialog/Host.vue +654 -0
- package/dist/runtime/components/Dialog/basic.demo.vue +27 -0
- package/dist/runtime/components/Dialog/form.demo.vue +34 -0
- package/dist/runtime/components/Dialog/index.vue +53 -0
- package/dist/runtime/components/Dialog/scroll.demo.vue +27 -0
- package/dist/runtime/components/Dialog/stack.demo.vue +19 -0
- package/dist/runtime/components/Form/rules.demo.vue +108 -0
- package/dist/runtime/components/Icon/gallery.demo.vue +1 -1
- package/dist/runtime/components/Icon/index.vue +3 -1
- package/dist/runtime/components/Input/index.vue +7 -5
- package/dist/runtime/components/InputNumber/controls.demo.vue +145 -0
- package/dist/runtime/components/InputNumber/index.vue +4 -3
- package/dist/runtime/composables/useDialog.d.ts +38 -0
- package/dist/runtime/composables/useDialog.js +40 -0
- package/dist/runtime/composables/useScreen.d.ts +8 -0
- package/dist/runtime/plugins/dialog.d.ts +3 -0
- package/dist/runtime/plugins/dialog.js +22 -0
- package/dist/runtime/tailwind.d.ts +1 -1
- package/dist/runtime/tailwind.js +2 -2
- package/dist/types.d.mts +2 -0
- package/package.json +6 -6
- package/dist/runtime/icon-font.d.ts +0 -4
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
description: 'Keep form state inside the dialog, require an explicit choice, and return the saved value.',
|
|
4
|
+
title: 'Form and explicit dismissal',
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { defineAsyncComponent, ref } from 'vue'
|
|
10
|
+
|
|
11
|
+
import { useDialog } from '../../composables/useDialog'
|
|
12
|
+
import Button from '../Button/index.vue'
|
|
13
|
+
|
|
14
|
+
const dialog = useDialog()
|
|
15
|
+
const savedName = ref('')
|
|
16
|
+
const LazyDemoForm = defineAsyncComponent(() => import('./DemoForm.vue'))
|
|
17
|
+
|
|
18
|
+
function openForm(): void {
|
|
19
|
+
dialog.open(LazyDemoForm, { onSave: (name: string) => (savedName.value = name) }, { preventClose: true })
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
25
|
+
<Button @click="openForm">Create project</Button>
|
|
26
|
+
<p
|
|
27
|
+
v-if="savedName"
|
|
28
|
+
class="text-sm text-zinc-300"
|
|
29
|
+
role="status"
|
|
30
|
+
>
|
|
31
|
+
Saved: {{ savedName }}
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { IconName } from '#brickflow-ui'
|
|
3
|
+
|
|
4
|
+
import type { DialogPlacement } from '../../composables/useDialog'
|
|
5
|
+
import type { ScreenKey } from '../../composables/useScreen'
|
|
6
|
+
|
|
7
|
+
export const dialogConfig = defineUiConfig<{
|
|
8
|
+
elasticHeight?: boolean
|
|
9
|
+
loadingDelay: number
|
|
10
|
+
loadingIconName: IconName
|
|
11
|
+
loadingIconTransition: string
|
|
12
|
+
placement: Record<ScreenKey, DialogPlacement>
|
|
13
|
+
}>()
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
defineProps<{
|
|
18
|
+
dragY?: number
|
|
19
|
+
elasticHeight: boolean
|
|
20
|
+
placement: 'bottom' | 'center' | 'fullscreen'
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const placementClasses = {
|
|
24
|
+
bottom: UI_STYLE.panelPlacementClasses.bottom,
|
|
25
|
+
center: UI_STYLE.panelPlacementClasses.center,
|
|
26
|
+
fullscreen: UI_STYLE.panelPlacementClasses.fullscreen,
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<div
|
|
32
|
+
:class="[UI_STYLE.panel, placementClasses[placement]]"
|
|
33
|
+
data-dialog-panel
|
|
34
|
+
role="dialog"
|
|
35
|
+
aria-modal="true"
|
|
36
|
+
tabindex="-1"
|
|
37
|
+
:style="{
|
|
38
|
+
'--dialog-elastic-height':
|
|
39
|
+
placement === 'bottom' && elasticHeight ? `${Math.max(0, -(dragY ?? 0))}px` : undefined,
|
|
40
|
+
transform: dragY && (!elasticHeight || dragY > 0) ? `translateY(${dragY}px)` : undefined,
|
|
41
|
+
}"
|
|
42
|
+
>
|
|
43
|
+
<div
|
|
44
|
+
v-if="placement === 'bottom'"
|
|
45
|
+
:class="UI_STYLE.dragHandle"
|
|
46
|
+
data-dialog-handle
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
>
|
|
49
|
+
<span :class="UI_STYLE.dragHandleIndicator" />
|
|
50
|
+
</div>
|
|
51
|
+
<slot />
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
description: 'The content component owns overflow. Scroll to an edge before dragging the sheet.',
|
|
4
|
+
title: 'Long content and scrolling',
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { defineAsyncComponent } from 'vue'
|
|
10
|
+
|
|
11
|
+
import { useDialog } from '../../composables/useDialog'
|
|
12
|
+
import Button from '../Button/index.vue'
|
|
13
|
+
|
|
14
|
+
const dialog = useDialog()
|
|
15
|
+
const LazyDemoContent = defineAsyncComponent(() => import('./DemoContent.vue'))
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<div class="flex flex-wrap gap-2">
|
|
20
|
+
<Button @click="dialog.open(LazyDemoContent, { text: 'Scrollable bottom sheet' }, { placement: 'bottom' })">
|
|
21
|
+
Scrollable sheet
|
|
22
|
+
</Button>
|
|
23
|
+
<Button @click="dialog.open(LazyDemoContent, { text: 'Scrollable centered dialog' }, { placement: 'center' })">
|
|
24
|
+
Scrollable center
|
|
25
|
+
</Button>
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
title: 'Stacked dialogs',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { defineAsyncComponent } from 'vue'
|
|
9
|
+
|
|
10
|
+
import { useDialog } from '../../composables/useDialog'
|
|
11
|
+
import Button from '../Button/index.vue'
|
|
12
|
+
|
|
13
|
+
const dialog = useDialog()
|
|
14
|
+
const LazyDemoStack = defineAsyncComponent(() => import('./DemoStack.vue'))
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<Button @click="dialog.open(LazyDemoStack, { level: 1 })">Open stack</Button>
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
description: 'Each field has one rule. Submit it to see that rule’s validation message.',
|
|
4
|
+
title: 'Basic rules',
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { reactive } from 'vue'
|
|
10
|
+
|
|
11
|
+
import { rule, type Rule } from '../../utils/rules'
|
|
12
|
+
import Button from '../Button/index.vue'
|
|
13
|
+
import Input from '../Input/index.vue'
|
|
14
|
+
import Form from './index.vue'
|
|
15
|
+
|
|
16
|
+
const demos: { description: string; id: string; label: string; placeholder: string; rules: Rule<string>[] }[] = [
|
|
17
|
+
{
|
|
18
|
+
description: 'Leave empty, or enter only spaces.',
|
|
19
|
+
id: 'required',
|
|
20
|
+
label: 'Required',
|
|
21
|
+
placeholder: 'Enter any text',
|
|
22
|
+
rules: [rule.required()],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
description: 'Try “hello”, then “hello@example.com”.',
|
|
26
|
+
id: 'email',
|
|
27
|
+
label: 'Email',
|
|
28
|
+
placeholder: 'hello@example.com',
|
|
29
|
+
rules: [rule.email()],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
description: 'Enter exactly 5 characters.',
|
|
33
|
+
id: 'length',
|
|
34
|
+
label: 'Length (5)',
|
|
35
|
+
placeholder: 'abcde',
|
|
36
|
+
rules: [rule.length(5)],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
description: 'Enter fewer than 5 characters, then 5 or more.',
|
|
40
|
+
id: 'minLength',
|
|
41
|
+
label: 'Minimum length (5)',
|
|
42
|
+
placeholder: 'At least 5 characters',
|
|
43
|
+
rules: [rule.minLength(5)],
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
description: 'Enter more than 5 characters.',
|
|
47
|
+
id: 'maxLength',
|
|
48
|
+
label: 'Maximum length (5)',
|
|
49
|
+
placeholder: 'Up to 5 characters',
|
|
50
|
+
rules: [rule.maxLength(5)],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
description: 'Enter letters or numbers, then add a space or symbol.',
|
|
54
|
+
id: 'regex',
|
|
55
|
+
label: 'Regex (letters and numbers)',
|
|
56
|
+
placeholder: 'abc123',
|
|
57
|
+
rules: [rule.regex(/^[a-z0-9]+$/i, 'Use letters and numbers only')],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
description: 'Try “example.com”, then “https://example.com”.',
|
|
61
|
+
id: 'url',
|
|
62
|
+
label: 'URL',
|
|
63
|
+
placeholder: 'https://example.com',
|
|
64
|
+
rules: [rule.url()],
|
|
65
|
+
},
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
const values = reactive<Record<string, string>>({})
|
|
69
|
+
const submitted = reactive<Record<string, boolean>>({})
|
|
70
|
+
|
|
71
|
+
function updateValue(id: string, value: string): void {
|
|
72
|
+
values[id] = value
|
|
73
|
+
submitted[id] = false
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<div class="grid gap-6">
|
|
79
|
+
<Form
|
|
80
|
+
v-for="demo in demos"
|
|
81
|
+
:key="demo.id"
|
|
82
|
+
class="rounded-lg"
|
|
83
|
+
@submit="submitted[demo.id] = true"
|
|
84
|
+
>
|
|
85
|
+
<Input
|
|
86
|
+
:value="values[demo.id] ?? ''"
|
|
87
|
+
:label="demo.label"
|
|
88
|
+
:description="demo.description"
|
|
89
|
+
:placeholder="demo.placeholder"
|
|
90
|
+
:rules="demo.rules"
|
|
91
|
+
@input="updateValue(demo.id, $event)"
|
|
92
|
+
/>
|
|
93
|
+
<Button
|
|
94
|
+
class="self-start"
|
|
95
|
+
type="submit"
|
|
96
|
+
>
|
|
97
|
+
Test rule
|
|
98
|
+
</Button>
|
|
99
|
+
<p
|
|
100
|
+
v-if="submitted[demo.id]"
|
|
101
|
+
class="text-sm text-green-400"
|
|
102
|
+
role="status"
|
|
103
|
+
>
|
|
104
|
+
Passed
|
|
105
|
+
</p>
|
|
106
|
+
</Form>
|
|
107
|
+
</div>
|
|
108
|
+
</template>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
const filteredIconNames = computed(() => {
|
|
21
21
|
const query = search.value.trim().toLocaleLowerCase()
|
|
22
22
|
|
|
23
|
-
return query ? iconNames.filter((name) => name.toLocaleLowerCase().includes(query)) : iconNames
|
|
23
|
+
return query ? iconNames.filter((name: string) => name.toLocaleLowerCase().includes(query)) : iconNames
|
|
24
24
|
})
|
|
25
25
|
|
|
26
26
|
const copyIconName = async (name: string): Promise<void> => {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { IconName } from '#brickflow-ui'
|
|
3
|
+
|
|
2
4
|
const UI_CONFIG = defineUiConfig<{
|
|
3
|
-
hidePasswordIconName:
|
|
4
|
-
searchClearIconName:
|
|
5
|
-
showPasswordIconName:
|
|
5
|
+
hidePasswordIconName: IconName
|
|
6
|
+
searchClearIconName: IconName
|
|
7
|
+
showPasswordIconName: IconName
|
|
6
8
|
sizeClasses: Record<
|
|
7
9
|
string,
|
|
8
10
|
{
|
|
@@ -51,7 +53,7 @@
|
|
|
51
53
|
autoWidth?: boolean
|
|
52
54
|
description?: string
|
|
53
55
|
disabled?: boolean
|
|
54
|
-
icon?:
|
|
56
|
+
icon?: IconName
|
|
55
57
|
label?: string
|
|
56
58
|
mask?: MaskInputOptions | MaskType
|
|
57
59
|
onBlur?: (value: string, target: HTMLInputElement) => void
|
|
@@ -65,7 +67,7 @@
|
|
|
65
67
|
readonly?: boolean
|
|
66
68
|
rules?: Rule<string>[]
|
|
67
69
|
size?: InputSize
|
|
68
|
-
trailingIcon?:
|
|
70
|
+
trailingIcon?: IconName
|
|
69
71
|
type?: 'decimal' | 'email' | 'numeric' | 'password' | 'search' | 'tel' | 'text' | 'url'
|
|
70
72
|
value?: null | string
|
|
71
73
|
variant?: InputVariant
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
description: 'Custom step buttons built with the leading and trailing slots.',
|
|
4
|
+
title: 'Custom controls',
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script lang="ts" setup>
|
|
9
|
+
import { ref } from 'vue'
|
|
10
|
+
|
|
11
|
+
import InputNumber from './index.vue'
|
|
12
|
+
|
|
13
|
+
const horizontalValue = ref(20)
|
|
14
|
+
const verticalValue = ref(100)
|
|
15
|
+
|
|
16
|
+
const horizontalMin = 0
|
|
17
|
+
const horizontalMax = 25
|
|
18
|
+
const verticalMin = 0
|
|
19
|
+
const verticalMax = 120
|
|
20
|
+
const verticalStep = 5
|
|
21
|
+
|
|
22
|
+
const horizontalControlClass =
|
|
23
|
+
'inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-plain-500 transition-colors enabled:hover:bg-plain-800 enabled:hover:text-plain-100 disabled:cursor-not-allowed disabled:opacity-40'
|
|
24
|
+
const verticalControlClass =
|
|
25
|
+
'flex min-h-5 flex-1 items-center justify-center text-plain-500 transition-colors enabled:hover:bg-plain-800 enabled:hover:text-plain-100 disabled:cursor-not-allowed disabled:opacity-40'
|
|
26
|
+
|
|
27
|
+
function stepHorizontal(direction: -1 | 1): void {
|
|
28
|
+
horizontalValue.value = Math.max(horizontalMin, Math.min(horizontalMax, horizontalValue.value + direction))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function stepVertical(direction: -1 | 1): void {
|
|
32
|
+
verticalValue.value = Math.max(
|
|
33
|
+
verticalMin,
|
|
34
|
+
Math.min(verticalMax, verticalValue.value + direction * verticalStep),
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<div class="flex max-w-sm flex-col gap-6">
|
|
41
|
+
<InputNumber
|
|
42
|
+
:value="horizontalValue"
|
|
43
|
+
align="center"
|
|
44
|
+
label="Number *"
|
|
45
|
+
description="This is a hint text to help user."
|
|
46
|
+
:min="horizontalMin"
|
|
47
|
+
:max="horizontalMax"
|
|
48
|
+
@input="horizontalValue = $event"
|
|
49
|
+
>
|
|
50
|
+
<template #leading>
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
:disabled="horizontalValue <= horizontalMin"
|
|
54
|
+
aria-label="Decrease number"
|
|
55
|
+
:class="[horizontalControlClass, '-ml-2']"
|
|
56
|
+
@click="stepHorizontal(-1)"
|
|
57
|
+
>
|
|
58
|
+
<span
|
|
59
|
+
aria-hidden="true"
|
|
60
|
+
class="text-2xl leading-none font-light"
|
|
61
|
+
>
|
|
62
|
+
−
|
|
63
|
+
</span>
|
|
64
|
+
</button>
|
|
65
|
+
</template>
|
|
66
|
+
<template #trailing>
|
|
67
|
+
<button
|
|
68
|
+
type="button"
|
|
69
|
+
:disabled="horizontalValue >= horizontalMax"
|
|
70
|
+
aria-label="Increase number"
|
|
71
|
+
:class="[horizontalControlClass, '-mr-2']"
|
|
72
|
+
@click="stepHorizontal(1)"
|
|
73
|
+
>
|
|
74
|
+
<span
|
|
75
|
+
aria-hidden="true"
|
|
76
|
+
class="text-2xl leading-none font-light"
|
|
77
|
+
>
|
|
78
|
+
+
|
|
79
|
+
</span>
|
|
80
|
+
</button>
|
|
81
|
+
</template>
|
|
82
|
+
</InputNumber>
|
|
83
|
+
|
|
84
|
+
<InputNumber
|
|
85
|
+
:value="verticalValue"
|
|
86
|
+
label="Number *"
|
|
87
|
+
description="This is a hint text to help user."
|
|
88
|
+
:step="verticalStep"
|
|
89
|
+
:min="verticalMin"
|
|
90
|
+
:max="verticalMax"
|
|
91
|
+
@input="verticalValue = $event"
|
|
92
|
+
>
|
|
93
|
+
<template #trailing>
|
|
94
|
+
<span
|
|
95
|
+
class="-mr-3 flex w-12 shrink-0 flex-col self-stretch overflow-hidden rounded-r-xl border-l border-neutral-600/20"
|
|
96
|
+
>
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
:disabled="verticalValue >= verticalMax"
|
|
100
|
+
aria-label="Increase number"
|
|
101
|
+
:class="verticalControlClass"
|
|
102
|
+
@click="stepVertical(1)"
|
|
103
|
+
>
|
|
104
|
+
<svg
|
|
105
|
+
aria-hidden="true"
|
|
106
|
+
class="size-4"
|
|
107
|
+
viewBox="0 0 16 16"
|
|
108
|
+
fill="none"
|
|
109
|
+
>
|
|
110
|
+
<path
|
|
111
|
+
d="m3 10 5-5 5 5"
|
|
112
|
+
stroke="currentColor"
|
|
113
|
+
stroke-width="2"
|
|
114
|
+
stroke-linecap="round"
|
|
115
|
+
stroke-linejoin="round"
|
|
116
|
+
/>
|
|
117
|
+
</svg>
|
|
118
|
+
</button>
|
|
119
|
+
<button
|
|
120
|
+
type="button"
|
|
121
|
+
:disabled="verticalValue <= verticalMin"
|
|
122
|
+
aria-label="Decrease number"
|
|
123
|
+
:class="[verticalControlClass, 'border-t border-neutral-600/20']"
|
|
124
|
+
@click="stepVertical(-1)"
|
|
125
|
+
>
|
|
126
|
+
<svg
|
|
127
|
+
aria-hidden="true"
|
|
128
|
+
class="size-4"
|
|
129
|
+
viewBox="0 0 16 16"
|
|
130
|
+
fill="none"
|
|
131
|
+
>
|
|
132
|
+
<path
|
|
133
|
+
d="m3 6 5 5 5-5"
|
|
134
|
+
stroke="currentColor"
|
|
135
|
+
stroke-width="2"
|
|
136
|
+
stroke-linecap="round"
|
|
137
|
+
stroke-linejoin="round"
|
|
138
|
+
/>
|
|
139
|
+
</svg>
|
|
140
|
+
</button>
|
|
141
|
+
</span>
|
|
142
|
+
</template>
|
|
143
|
+
</InputNumber>
|
|
144
|
+
</div>
|
|
145
|
+
</template>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts" generic="T extends boolean = false">
|
|
2
2
|
import { computed, ref, shallowRef, watch } from 'vue'
|
|
3
3
|
|
|
4
|
+
import type { IconName } from '#brickflow-ui'
|
|
5
|
+
|
|
4
6
|
import type { Rule } from '../../utils/rules'
|
|
5
7
|
|
|
6
8
|
import { isEmpty } from '../../utils/checkup.js'
|
|
@@ -16,7 +18,7 @@
|
|
|
16
18
|
denominator?: number
|
|
17
19
|
description?: string
|
|
18
20
|
disabled?: boolean
|
|
19
|
-
icon?:
|
|
21
|
+
icon?: IconName
|
|
20
22
|
label?: string
|
|
21
23
|
max?: number
|
|
22
24
|
min?: number
|
|
@@ -33,7 +35,7 @@
|
|
|
33
35
|
rules?: Rule<null | number>[]
|
|
34
36
|
size?: InputProps['size']
|
|
35
37
|
step?: number
|
|
36
|
-
trailingIcon?:
|
|
38
|
+
trailingIcon?: IconName
|
|
37
39
|
value: true extends NoInfer<T> ? null | number : number
|
|
38
40
|
variant?: InputProps['variant']
|
|
39
41
|
}>(),
|
|
@@ -176,7 +178,6 @@
|
|
|
176
178
|
if (!Number.isFinite(props.step) || props.step <= 0) {
|
|
177
179
|
return
|
|
178
180
|
}
|
|
179
|
-
|
|
180
181
|
event.preventDefault()
|
|
181
182
|
const parsedValue = Number(target.value)
|
|
182
183
|
const currentValue = target.value !== '' && Number.isFinite(parsedValue) ? parsedValue : (props.value ?? 0)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type Component, type InjectionKey } from 'vue';
|
|
2
|
+
import type { ScreenKey } from './useScreen.js';
|
|
3
|
+
export interface DialogController {
|
|
4
|
+
close(id?: number): void;
|
|
5
|
+
closeAll(): void;
|
|
6
|
+
open<C extends Component>(component: C, props: DialogProps<C>, options?: DialogOptions): DialogHandle;
|
|
7
|
+
readonly stack: DialogEntry[];
|
|
8
|
+
}
|
|
9
|
+
export interface DialogEntry {
|
|
10
|
+
component: Component;
|
|
11
|
+
id: number;
|
|
12
|
+
options: Required<DialogOptions> & {
|
|
13
|
+
elasticHeight: boolean;
|
|
14
|
+
};
|
|
15
|
+
props: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export interface DialogHandle {
|
|
18
|
+
close: () => void;
|
|
19
|
+
id: number;
|
|
20
|
+
}
|
|
21
|
+
export interface DialogOptions {
|
|
22
|
+
/** Dialog position, optionally overridden for each screen range. Defaults to center. */
|
|
23
|
+
placement?: DialogPlacementConfig;
|
|
24
|
+
/** Prevent backdrop, Escape, and swipe dismissal. Defaults to false. */
|
|
25
|
+
preventClose?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export type DialogPlacement = 'bottom' | 'center' | 'fullscreen';
|
|
28
|
+
export type DialogPlacementConfig = DialogPlacement | Record<ScreenKey, DialogPlacement>;
|
|
29
|
+
type DialogProps<C> = C extends new (...args: never[]) => {
|
|
30
|
+
$props: infer P;
|
|
31
|
+
} ? Omit<P, 'class' | 'key' | 'ref' | 'style'> : Record<string, unknown>;
|
|
32
|
+
export declare const dialogKey: InjectionKey<DialogController>;
|
|
33
|
+
export declare function createDialogController(defaultOptions?: DialogOptions & {
|
|
34
|
+
elasticHeight?: boolean;
|
|
35
|
+
}): DialogController;
|
|
36
|
+
export declare function useDialog(): DialogController;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=useDialog.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { inject, markRaw, reactive } from "vue";
|
|
2
|
+
export const dialogKey = Symbol("brickflow-dialog");
|
|
3
|
+
export function createDialogController(defaultOptions = {}) {
|
|
4
|
+
const stack = reactive([]);
|
|
5
|
+
let nextId = 0;
|
|
6
|
+
return {
|
|
7
|
+
close(id) {
|
|
8
|
+
const index = id === void 0 ? stack.length - 1 : stack.findIndex((entry) => entry.id === id);
|
|
9
|
+
if (index !== -1) {
|
|
10
|
+
stack.splice(index, 1);
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
closeAll() {
|
|
14
|
+
stack.splice(0);
|
|
15
|
+
},
|
|
16
|
+
open(component, props, options = {}) {
|
|
17
|
+
nextId += 1;
|
|
18
|
+
const id = nextId;
|
|
19
|
+
stack.push({
|
|
20
|
+
component: markRaw(component),
|
|
21
|
+
id,
|
|
22
|
+
options: {
|
|
23
|
+
elasticHeight: defaultOptions.elasticHeight ?? true,
|
|
24
|
+
placement: options.placement ?? defaultOptions.placement ?? "center",
|
|
25
|
+
preventClose: options.preventClose ?? defaultOptions.preventClose ?? false
|
|
26
|
+
},
|
|
27
|
+
props: { ...props }
|
|
28
|
+
});
|
|
29
|
+
return { close: () => this.close(id), id };
|
|
30
|
+
},
|
|
31
|
+
stack
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function useDialog() {
|
|
35
|
+
const dialog = inject(dialogKey, void 0);
|
|
36
|
+
if (!dialog) {
|
|
37
|
+
throw new Error("useDialog() requires the @brickflow/ui Nuxt module");
|
|
38
|
+
}
|
|
39
|
+
return dialog;
|
|
40
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineNuxtPlugin } from "nuxt/app";
|
|
2
|
+
import { createVNode, render } from "vue";
|
|
3
|
+
import { uiConfig } from "#brickflow-ui";
|
|
4
|
+
import DialogHost from "../components/Dialog/Host.vue";
|
|
5
|
+
import { createDialogController, dialogKey } from "../composables/useDialog.js";
|
|
6
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
7
|
+
const dialog = createDialogController({
|
|
8
|
+
elasticHeight: uiConfig.dialog.elasticHeight,
|
|
9
|
+
placement: uiConfig.dialog.placement
|
|
10
|
+
});
|
|
11
|
+
nuxtApp.vueApp.provide(dialogKey, dialog);
|
|
12
|
+
if (import.meta.client) {
|
|
13
|
+
nuxtApp.hook("app:mounted", () => {
|
|
14
|
+
const mountPoint = document.createElement("div");
|
|
15
|
+
mountPoint.id = "brickflow-dialog-host";
|
|
16
|
+
document.body.appendChild(mountPoint);
|
|
17
|
+
const vnode = createVNode(DialogHost, { dialog });
|
|
18
|
+
vnode.appContext = nuxtApp.vueApp._context;
|
|
19
|
+
render(vnode, mountPoint);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -6,7 +6,7 @@ export interface BrickflowUiConfig<TStyles = BrickflowUiStyles, TConfig = Brickf
|
|
|
6
6
|
export type BrickflowUiConfigObject = {
|
|
7
7
|
readonly [key: string]: BrickflowUiConfigValue;
|
|
8
8
|
};
|
|
9
|
-
export type BrickflowUiConfigValue = BrickflowUiConfigObject | BrickflowUiStyleReference | string;
|
|
9
|
+
export type BrickflowUiConfigValue = boolean | BrickflowUiConfigObject | BrickflowUiStyleReference | number | string;
|
|
10
10
|
export interface BrickflowUiStyleReference {
|
|
11
11
|
readonly [UI_STYLE_REFERENCE]: readonly string[];
|
|
12
12
|
}
|
package/dist/runtime/tailwind.js
CHANGED
|
@@ -44,14 +44,14 @@ function validateUiConfig(value, path = "uiConfig") {
|
|
|
44
44
|
}
|
|
45
45
|
for (const [key, childValue] of Object.entries(value)) {
|
|
46
46
|
const childPath = `${path}.${key}`;
|
|
47
|
-
if (typeof childValue === "string" || isBrickflowUiStyleReference(childValue)) {
|
|
47
|
+
if (typeof childValue === "string" || typeof childValue === "boolean" || typeof childValue === "number" && Number.isFinite(childValue) || isBrickflowUiStyleReference(childValue)) {
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
50
|
if (isRecord(childValue)) {
|
|
51
51
|
validateUiConfig(childValue, childPath);
|
|
52
52
|
continue;
|
|
53
53
|
}
|
|
54
|
-
throw new TypeError(`${childPath} must be a string, UI_STYLE reference, or an object.`);
|
|
54
|
+
throw new TypeError(`${childPath} must be a string, boolean, finite number, UI_STYLE reference, or an object.`);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
function validateUiStyles(value, path = "uiStyles") {
|
package/dist/types.d.mts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { type BrickflowConfig } from '../dist/runtime/composables/useBrickflow.js'
|
|
2
2
|
|
|
3
|
+
export { type DialogController, type DialogHandle, type DialogOptions } from '../dist/runtime/composables/useDialog.js'
|
|
4
|
+
|
|
3
5
|
export { type BrickflowI18n, type BrickflowRouteLocationParam } from '../dist/runtime/composables/useTranslate.js'
|
|
4
6
|
|
|
5
7
|
export { type Rule, type RuleMessages, type rule } from '../dist/runtime/utils/rules.js'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brickflow/ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.58",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "brickflow UI Nuxt module.",
|
|
6
6
|
"files": [
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
"fantasticon": "4.1.0",
|
|
37
37
|
"jiti": "2.7.0",
|
|
38
38
|
"maska": "3.2.2",
|
|
39
|
-
"tailwind-merge": "3.
|
|
39
|
+
"tailwind-merge": "3.7.0",
|
|
40
40
|
"tailwindcss": "4.3.3"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@brickflow/lint": "workspace:*",
|
|
44
44
|
"@brickflow/prettier": "workspace:*",
|
|
45
|
-
"@nuxt/kit": "4.
|
|
45
|
+
"@nuxt/kit": "4.6.3",
|
|
46
46
|
"@nuxt/module-builder": "1.0.3",
|
|
47
|
-
"nuxt": "4.
|
|
48
|
-
"vue": "3.5.
|
|
49
|
-
"vue-tsc": "3.3.
|
|
47
|
+
"nuxt": "4.6.3",
|
|
48
|
+
"vue": "3.5.44",
|
|
49
|
+
"vue-tsc": "3.3.14"
|
|
50
50
|
},
|
|
51
51
|
"author": "",
|
|
52
52
|
"license": "ISC"
|