@brickflow/ui 0.0.56 → 0.0.57
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 +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -0
- package/dist/runtime/components/Form/index.vue +46 -1
- package/dist/runtime/components/Form/login.demo.vue +12 -1
- package/dist/runtime/components/Input/index.vue +144 -22
- package/dist/runtime/components/Input/label.demo.vue +15 -3
- package/dist/runtime/components/Input/mask.demo.vue +73 -0
- package/dist/runtime/components/Input/variants.demo.vue +16 -0
- package/dist/runtime/components/InputNumber/denominator.demo.vue +1 -1
- package/dist/runtime/components/InputNumber/index.vue +25 -5
- package/dist/runtime/components/TransitionSize/duration.demo.vue +88 -0
- package/dist/runtime/components/TransitionSize/flex.demo.vue +112 -0
- package/dist/runtime/components/TransitionSize/height.demo.vue +88 -0
- package/dist/runtime/components/TransitionSize/index.vue +69 -27
- package/dist/runtime/components/TransitionSize/width.demo.vue +88 -0
- package/dist/runtime/composables/useBrickflow.d.ts +2 -0
- package/dist/runtime/composables/useFieldRules.d.ts +16 -0
- package/dist/runtime/composables/useFieldRules.js +42 -0
- package/dist/runtime/utils/rules.d.ts +34 -0
- package/dist/runtime/utils/rules.js +68 -0
- package/dist/types.d.mts +2 -0
- package/package.json +2 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
title: 'Duration',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
import TransitionSize from './index.vue'
|
|
11
|
+
|
|
12
|
+
const active100 = ref(true)
|
|
13
|
+
const active500 = ref(true)
|
|
14
|
+
const active2000 = ref(true)
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div class="flex w-full flex-col">
|
|
19
|
+
<code class="mb-2">100</code>
|
|
20
|
+
<div class="flex w-full">
|
|
21
|
+
<TransitionSize
|
|
22
|
+
:duration="100"
|
|
23
|
+
dimension="width"
|
|
24
|
+
>
|
|
25
|
+
<div
|
|
26
|
+
v-if="active100"
|
|
27
|
+
class="w-full rounded-xl bg-slate-700 p-2 text-center"
|
|
28
|
+
>
|
|
29
|
+
Text
|
|
30
|
+
</div>
|
|
31
|
+
</TransitionSize>
|
|
32
|
+
<button
|
|
33
|
+
type="button"
|
|
34
|
+
class="ml-2 w-30 shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
35
|
+
:class="active100 ? 'bg-blue-500' : 'bg-blue-800'"
|
|
36
|
+
@click="active100 = !active100"
|
|
37
|
+
>
|
|
38
|
+
{{ active100 ? 'Hide' : 'Show' }}
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<code class="mt-4 mb-2">500</code>
|
|
43
|
+
<div class="flex">
|
|
44
|
+
<TransitionSize
|
|
45
|
+
:duration="500"
|
|
46
|
+
dimension="width"
|
|
47
|
+
>
|
|
48
|
+
<div
|
|
49
|
+
v-if="active500"
|
|
50
|
+
class="w-full rounded-xl bg-slate-700 p-2 text-center whitespace-nowrap"
|
|
51
|
+
>
|
|
52
|
+
Text
|
|
53
|
+
</div>
|
|
54
|
+
</TransitionSize>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
class="ml-2 w-30 shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
58
|
+
:class="active500 ? 'bg-blue-500' : 'bg-blue-800'"
|
|
59
|
+
@click="active500 = !active500"
|
|
60
|
+
>
|
|
61
|
+
{{ active500 ? 'Hide' : 'Show' }}
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<code class="mt-4 mb-2">2000</code>
|
|
66
|
+
<div class="flex w-full">
|
|
67
|
+
<TransitionSize
|
|
68
|
+
:duration="2000"
|
|
69
|
+
dimension="width"
|
|
70
|
+
>
|
|
71
|
+
<div
|
|
72
|
+
v-if="active2000"
|
|
73
|
+
class="w-full rounded-xl bg-slate-700 p-2 text-center"
|
|
74
|
+
>
|
|
75
|
+
Text
|
|
76
|
+
</div>
|
|
77
|
+
</TransitionSize>
|
|
78
|
+
<button
|
|
79
|
+
type="button"
|
|
80
|
+
class="ml-2 w-30 shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
81
|
+
:class="active2000 ? 'bg-blue-500' : 'bg-blue-800'"
|
|
82
|
+
@click="active2000 = !active2000"
|
|
83
|
+
>
|
|
84
|
+
{{ active2000 ? 'Hide' : 'Show' }}
|
|
85
|
+
</button>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</template>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
title: 'Flex Parent with Gap',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
import TransitionSize from './index.vue'
|
|
11
|
+
|
|
12
|
+
const baseStartRef = ref<HTMLElement | null>(null)
|
|
13
|
+
const baseCenterRef = ref<HTMLElement | null>(null)
|
|
14
|
+
const baseEndRef = ref<HTMLElement | null>(null)
|
|
15
|
+
const activeStart = ref(true)
|
|
16
|
+
const activeCenter = ref(true)
|
|
17
|
+
const activeEnd = ref(true)
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<div class="flex flex-col">
|
|
22
|
+
<code class="mb-2">flexDirection="start"</code>
|
|
23
|
+
<div
|
|
24
|
+
ref="baseStartRef"
|
|
25
|
+
class="flex grow basis-1 gap-10"
|
|
26
|
+
>
|
|
27
|
+
<TransitionSize
|
|
28
|
+
:duration="1000"
|
|
29
|
+
:flexParent="baseStartRef"
|
|
30
|
+
flexDirection="start"
|
|
31
|
+
dimension="width"
|
|
32
|
+
>
|
|
33
|
+
<div
|
|
34
|
+
v-if="activeStart"
|
|
35
|
+
class="h-10 grow rounded-xl bg-slate-700 p-2 text-center"
|
|
36
|
+
>
|
|
37
|
+
Text
|
|
38
|
+
</div>
|
|
39
|
+
</TransitionSize>
|
|
40
|
+
<div class="h-10 grow rounded-xl bg-slate-800 p-2 text-center">Text</div>
|
|
41
|
+
<div class="h-10 grow rounded-xl bg-slate-800 p-2 text-center">Text</div>
|
|
42
|
+
</div>
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
class="mt-3 w-30 shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
46
|
+
:class="activeStart ? 'bg-blue-500' : 'bg-blue-800'"
|
|
47
|
+
@click="activeStart = !activeStart"
|
|
48
|
+
>
|
|
49
|
+
{{ activeStart ? 'Hide' : 'Show' }}
|
|
50
|
+
</button>
|
|
51
|
+
|
|
52
|
+
<code class="mt-6 mb-2">flexDirection="center"</code>
|
|
53
|
+
<div
|
|
54
|
+
ref="baseCenterRef"
|
|
55
|
+
class="flex grow basis-1 gap-10"
|
|
56
|
+
>
|
|
57
|
+
<div class="h-10 grow rounded-xl bg-slate-800 p-2 text-center">Text</div>
|
|
58
|
+
<TransitionSize
|
|
59
|
+
:duration="1000"
|
|
60
|
+
:flexParent="baseCenterRef"
|
|
61
|
+
flexDirection="center"
|
|
62
|
+
dimension="width"
|
|
63
|
+
>
|
|
64
|
+
<div
|
|
65
|
+
v-if="activeCenter"
|
|
66
|
+
class="h-10 grow rounded-xl bg-slate-700 p-2 text-center"
|
|
67
|
+
>
|
|
68
|
+
Text
|
|
69
|
+
</div>
|
|
70
|
+
</TransitionSize>
|
|
71
|
+
<div class="h-10 grow rounded-xl bg-slate-800 p-2 text-center">Text</div>
|
|
72
|
+
</div>
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
class="mt-3 w-30 shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
76
|
+
:class="activeCenter ? 'bg-blue-500' : 'bg-blue-800'"
|
|
77
|
+
@click="activeCenter = !activeCenter"
|
|
78
|
+
>
|
|
79
|
+
{{ activeCenter ? 'Hide' : 'Show' }}
|
|
80
|
+
</button>
|
|
81
|
+
|
|
82
|
+
<code class="mt-6 mb-2">flexDirection="end"</code>
|
|
83
|
+
<div
|
|
84
|
+
ref="baseEndRef"
|
|
85
|
+
class="flex grow basis-1 gap-10"
|
|
86
|
+
>
|
|
87
|
+
<div class="h-10 grow rounded-xl bg-slate-800 p-2 text-center">Text</div>
|
|
88
|
+
<div class="h-10 grow rounded-xl bg-slate-800 p-2 text-center">Text</div>
|
|
89
|
+
<TransitionSize
|
|
90
|
+
:duration="1000"
|
|
91
|
+
:flexParent="baseEndRef"
|
|
92
|
+
flexDirection="end"
|
|
93
|
+
dimension="width"
|
|
94
|
+
>
|
|
95
|
+
<div
|
|
96
|
+
v-if="activeEnd"
|
|
97
|
+
class="h-10 grow rounded-xl bg-slate-700 p-2 text-center"
|
|
98
|
+
>
|
|
99
|
+
Text
|
|
100
|
+
</div>
|
|
101
|
+
</TransitionSize>
|
|
102
|
+
</div>
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
class="mt-3 w-30 shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
106
|
+
:class="activeEnd ? 'bg-blue-500' : 'bg-blue-800'"
|
|
107
|
+
@click="activeEnd = !activeEnd"
|
|
108
|
+
>
|
|
109
|
+
{{ activeEnd ? 'Hide' : 'Show' }}
|
|
110
|
+
</button>
|
|
111
|
+
</div>
|
|
112
|
+
</template>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
title: 'Height',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
import TransitionSize from './index.vue'
|
|
11
|
+
|
|
12
|
+
const activeHeightFixed = ref(true)
|
|
13
|
+
const activeHeightContent = ref(true)
|
|
14
|
+
const activeHeightFull = ref(true)
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div class="flex w-full gap-4">
|
|
19
|
+
<div class="flex grow basis-1 flex-col">
|
|
20
|
+
<label class="mb-2">Fixed height</label>
|
|
21
|
+
<TransitionSize
|
|
22
|
+
:duration="1000"
|
|
23
|
+
dimension="height"
|
|
24
|
+
>
|
|
25
|
+
<div
|
|
26
|
+
v-if="activeHeightFixed"
|
|
27
|
+
class="h-40 w-full rounded-xl bg-slate-700 p-2 text-center"
|
|
28
|
+
>
|
|
29
|
+
Text
|
|
30
|
+
</div>
|
|
31
|
+
</TransitionSize>
|
|
32
|
+
<button
|
|
33
|
+
type="button"
|
|
34
|
+
class="mt-3 w-full shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
35
|
+
:class="activeHeightFixed ? 'bg-blue-500' : 'bg-blue-800'"
|
|
36
|
+
@click="activeHeightFixed = !activeHeightFixed"
|
|
37
|
+
>
|
|
38
|
+
{{ activeHeightFixed ? 'Hide' : 'Show' }}
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="flex grow basis-1 flex-col">
|
|
43
|
+
<label class="mb-2">Content based height</label>
|
|
44
|
+
<TransitionSize
|
|
45
|
+
:duration="1000"
|
|
46
|
+
dimension="height"
|
|
47
|
+
>
|
|
48
|
+
<div
|
|
49
|
+
v-if="activeHeightContent"
|
|
50
|
+
class="rounded-xl bg-slate-700 p-2 text-center"
|
|
51
|
+
>
|
|
52
|
+
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Tenetur nihil possimus sunt ipsum rerum maiores
|
|
53
|
+
</div>
|
|
54
|
+
</TransitionSize>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
class="mt-3 w-full shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
58
|
+
:class="activeHeightContent ? 'bg-blue-500' : 'bg-blue-800'"
|
|
59
|
+
@click="activeHeightContent = !activeHeightContent"
|
|
60
|
+
>
|
|
61
|
+
{{ activeHeightContent ? 'Hide' : 'Show' }}
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div class="flex h-auto grow basis-1 flex-col self-stretch">
|
|
66
|
+
<label class="mb-2">Full height</label>
|
|
67
|
+
<TransitionSize
|
|
68
|
+
:duration="1000"
|
|
69
|
+
dimension="height"
|
|
70
|
+
>
|
|
71
|
+
<div
|
|
72
|
+
v-if="activeHeightFull"
|
|
73
|
+
class="w-full grow rounded-xl bg-slate-700 p-2 text-center"
|
|
74
|
+
>
|
|
75
|
+
Text
|
|
76
|
+
</div>
|
|
77
|
+
</TransitionSize>
|
|
78
|
+
<button
|
|
79
|
+
type="button"
|
|
80
|
+
class="mt-3 w-full shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
81
|
+
:class="activeHeightFull ? 'bg-blue-500' : 'bg-blue-800'"
|
|
82
|
+
@click="activeHeightFull = !activeHeightFull"
|
|
83
|
+
>
|
|
84
|
+
{{ activeHeightFull ? 'Hide' : 'Show' }}
|
|
85
|
+
</button>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</template>
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
dimension?: 'height' | 'width'
|
|
22
22
|
duration?: number
|
|
23
23
|
expandClasses?: ''
|
|
24
|
-
flexDirection?: 'end' | 'start'
|
|
25
|
-
flexParent?: Element
|
|
24
|
+
flexDirection?: 'center' | 'end' | 'start'
|
|
25
|
+
flexParent?: Element | null
|
|
26
26
|
mode?: 'default' | 'in-out' | 'out-in'
|
|
27
27
|
name?: string
|
|
28
28
|
}>(),
|
|
@@ -41,21 +41,14 @@
|
|
|
41
41
|
const gap = ref(0)
|
|
42
42
|
|
|
43
43
|
const sizeName = computed(() => props.dimension)
|
|
44
|
-
const
|
|
45
|
-
const xDirection = props.flexDirection === 'start' ? 'marginLeft' : 'marginRight'
|
|
46
|
-
const yDirection = props.flexDirection === 'start' ? 'marginTop' : 'marginBottom'
|
|
47
|
-
return props.dimension === 'height' ? yDirection : xDirection
|
|
48
|
-
})
|
|
49
|
-
const scrollName = computed(() => (props.dimension === 'height' ? 'scrollHeight' : 'scrollWidth'))
|
|
44
|
+
const scrollName = computed(() => (props.dimension === 'height' ? 'offsetHeight' : 'offsetWidth'))
|
|
50
45
|
const expandClass = computed(() => {
|
|
51
|
-
return (props.expandClasses || '!transition-all !ease-in-out
|
|
52
|
-
' ',
|
|
53
|
-
)
|
|
46
|
+
return (props.expandClasses || '!transition-all !ease-in-out').split(' ')
|
|
54
47
|
})
|
|
55
48
|
|
|
56
49
|
const beforeStart = (event: Element): void => {
|
|
57
50
|
const el = event as HTMLElement
|
|
58
|
-
|
|
51
|
+
el.style.setProperty('transition-duration', `${0}ms`, 'important')
|
|
59
52
|
}
|
|
60
53
|
const start = (event: Element): void => {
|
|
61
54
|
const el = event as HTMLElement
|
|
@@ -63,16 +56,22 @@
|
|
|
63
56
|
|
|
64
57
|
// Wait until component mounted to get proper height
|
|
65
58
|
nextTick(() => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
el.style.padding = ''
|
|
71
|
-
if (props.flexParent) {
|
|
72
|
-
el.style[marginName.value] = ''
|
|
73
|
-
}
|
|
74
|
-
}
|
|
59
|
+
releasePadding(el)
|
|
60
|
+
const height = el[scrollName.value]
|
|
61
|
+
shrinkPadding(el)
|
|
62
|
+
shrink(el)
|
|
75
63
|
el.style.overflow = 'hidden'
|
|
64
|
+
requestAnimationFrame(() => {
|
|
65
|
+
el.style.setProperty('transition-duration', `${props.duration}ms`, 'important')
|
|
66
|
+
if (height === 0) {
|
|
67
|
+
el.style[sizeName.value] = ''
|
|
68
|
+
releasePadding(el)
|
|
69
|
+
} else {
|
|
70
|
+
el.style[sizeName.value] = `${height}px`
|
|
71
|
+
releasePadding(el)
|
|
72
|
+
setNegativeMargin(el, 0)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
76
75
|
})
|
|
77
76
|
}
|
|
78
77
|
const afterEnter = (event: Element): void => {
|
|
@@ -86,13 +85,15 @@
|
|
|
86
85
|
|
|
87
86
|
el.style[sizeName.value] = `${el[scrollName.value]}px`
|
|
88
87
|
if (props.flexParent) {
|
|
89
|
-
el.style
|
|
88
|
+
el.style.setProperty('flex-shrink', '0', 'important')
|
|
89
|
+
el.style.setProperty('flex-grow', '0', 'important')
|
|
90
90
|
}
|
|
91
91
|
el.style.overflow = 'hidden'
|
|
92
92
|
}
|
|
93
93
|
const leave = (event: Element): void => {
|
|
94
94
|
const el = event as HTMLElement
|
|
95
95
|
if (el[scrollName.value] !== 0) {
|
|
96
|
+
el.style.setProperty('transition-duration', `${props.duration}ms`, 'important')
|
|
96
97
|
shrink(el)
|
|
97
98
|
}
|
|
98
99
|
}
|
|
@@ -102,20 +103,61 @@
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
function release(el: HTMLElement): void {
|
|
105
|
-
el.classList.
|
|
106
|
+
el.classList.remove(...expandClass.value)
|
|
106
107
|
el.style.transitionDuration = ''
|
|
108
|
+
el.style.flexGrow = ''
|
|
109
|
+
el.style.flexShrink = ''
|
|
107
110
|
el.style[sizeName.value] = ''
|
|
108
|
-
el.style[marginName.value] = ''
|
|
109
111
|
el.style.overflow = String(el.dataset.oldOverflow)
|
|
112
|
+
setNegativeMargin(el, 0)
|
|
113
|
+
releasePadding(el)
|
|
114
|
+
}
|
|
115
|
+
function releasePadding(el: HTMLElement): void {
|
|
116
|
+
if (props.dimension === 'height') {
|
|
117
|
+
el.style.paddingTop = ''
|
|
118
|
+
el.style.paddingBottom = ''
|
|
119
|
+
} else {
|
|
120
|
+
el.style.paddingLeft = ''
|
|
121
|
+
el.style.paddingRight = ''
|
|
122
|
+
}
|
|
110
123
|
}
|
|
124
|
+
|
|
125
|
+
function setNegativeMargin(el: HTMLElement, value?: number): void {
|
|
126
|
+
if (value === undefined || !props.flexParent) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
switch (props.flexDirection) {
|
|
130
|
+
case 'center':
|
|
131
|
+
el.style[props.dimension === 'height' ? 'marginTop' : 'marginLeft'] = value ? `-${value / 2}px` : ''
|
|
132
|
+
el.style[props.dimension === 'height' ? 'marginBottom' : 'marginRight'] = value ? `-${value / 2}px` : ''
|
|
133
|
+
break
|
|
134
|
+
case 'end':
|
|
135
|
+
el.style[props.dimension === 'height' ? 'marginTop' : 'marginLeft'] = value ? `-${value}px` : ''
|
|
136
|
+
break
|
|
137
|
+
case 'start':
|
|
138
|
+
el.style[props.dimension === 'height' ? 'marginBottom' : 'marginRight'] = value ? `-${value}px` : ''
|
|
139
|
+
break
|
|
140
|
+
default:
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
111
144
|
function shrink(el: HTMLElement): void {
|
|
112
145
|
el.classList.add(...expandClass.value)
|
|
113
|
-
el.style.
|
|
146
|
+
el.style.flexGrow = '0'
|
|
114
147
|
el.style[sizeName.value] = '0'
|
|
115
|
-
el
|
|
148
|
+
shrinkPadding(el)
|
|
116
149
|
if (props.flexParent) {
|
|
117
150
|
gap.value = parseInt(window.getComputedStyle(props.flexParent)?.gap) || 0
|
|
118
|
-
el
|
|
151
|
+
setNegativeMargin(el, gap.value)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function shrinkPadding(el: HTMLElement): void {
|
|
155
|
+
if (props.dimension === 'height') {
|
|
156
|
+
el.style.paddingTop = '0'
|
|
157
|
+
el.style.paddingBottom = '0'
|
|
158
|
+
} else {
|
|
159
|
+
el.style.paddingLeft = '0'
|
|
160
|
+
el.style.paddingRight = '0'
|
|
119
161
|
}
|
|
120
162
|
}
|
|
121
163
|
</script>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
title: 'Width',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
import TransitionSize from './index.vue'
|
|
11
|
+
|
|
12
|
+
const activeWidthFixed = ref(true)
|
|
13
|
+
const activeWidthContent = ref(true)
|
|
14
|
+
const activeWidthFull = ref(true)
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div class="flex w-full flex-col">
|
|
19
|
+
<label class="mb-2">Fixed width</label>
|
|
20
|
+
<div class="flex w-full">
|
|
21
|
+
<TransitionSize
|
|
22
|
+
:duration="1000"
|
|
23
|
+
dimension="width"
|
|
24
|
+
>
|
|
25
|
+
<div
|
|
26
|
+
v-if="activeWidthFixed"
|
|
27
|
+
class="w-30 rounded-xl bg-slate-700 p-2 text-center"
|
|
28
|
+
>
|
|
29
|
+
Text
|
|
30
|
+
</div>
|
|
31
|
+
</TransitionSize>
|
|
32
|
+
<button
|
|
33
|
+
type="button"
|
|
34
|
+
class="ml-2 w-30 shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
35
|
+
:class="activeWidthFixed ? 'bg-blue-500' : 'bg-blue-800'"
|
|
36
|
+
@click="activeWidthFixed = !activeWidthFixed"
|
|
37
|
+
>
|
|
38
|
+
{{ activeWidthFixed ? 'Hide' : 'Show' }}
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<label class="mt-4 mb-2">Content based width</label>
|
|
43
|
+
<div class="flex">
|
|
44
|
+
<TransitionSize
|
|
45
|
+
:duration="1000"
|
|
46
|
+
dimension="width"
|
|
47
|
+
>
|
|
48
|
+
<div
|
|
49
|
+
v-if="activeWidthContent"
|
|
50
|
+
class="rounded-xl bg-slate-700 p-2 text-center whitespace-nowrap"
|
|
51
|
+
>
|
|
52
|
+
Long content text
|
|
53
|
+
</div>
|
|
54
|
+
</TransitionSize>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
class="ml-2 w-30 shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
58
|
+
:class="activeWidthContent ? 'bg-blue-500' : 'bg-blue-800'"
|
|
59
|
+
@click="activeWidthContent = !activeWidthContent"
|
|
60
|
+
>
|
|
61
|
+
{{ activeWidthContent ? 'Hide' : 'Show' }}
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<label class="mt-4 mb-2">Full width</label>
|
|
66
|
+
<div class="flex w-full">
|
|
67
|
+
<TransitionSize
|
|
68
|
+
:duration="1000"
|
|
69
|
+
dimension="width"
|
|
70
|
+
>
|
|
71
|
+
<div
|
|
72
|
+
v-if="activeWidthFull"
|
|
73
|
+
class="w-full rounded-xl bg-slate-700 p-2 text-center"
|
|
74
|
+
>
|
|
75
|
+
Text
|
|
76
|
+
</div>
|
|
77
|
+
</TransitionSize>
|
|
78
|
+
<button
|
|
79
|
+
type="button"
|
|
80
|
+
class="ml-2 w-30 shrink-0 cursor-pointer rounded-xl px-3 py-2"
|
|
81
|
+
:class="activeWidthFull ? 'bg-blue-500' : 'bg-blue-800'"
|
|
82
|
+
@click="activeWidthFull = !activeWidthFull"
|
|
83
|
+
>
|
|
84
|
+
{{ activeWidthFull ? 'Hide' : 'Show' }}
|
|
85
|
+
</button>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</template>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type App } from 'vue';
|
|
2
|
+
import type { RuleMessages } from '../utils/rules.js';
|
|
2
3
|
import type { BrickflowI18n } from './useTranslate.js';
|
|
3
4
|
export interface BrickflowConfig {
|
|
4
5
|
i18n?: BrickflowI18n;
|
|
6
|
+
rules?: RuleMessages;
|
|
5
7
|
}
|
|
6
8
|
export declare function provideBrickflow<T extends BrickflowConfig>(app: App, config: T): void;
|
|
7
9
|
export declare function useBrickflow(): BrickflowConfig;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type InjectionKey, type Ref } from 'vue';
|
|
2
|
+
import type { Rule } from '../utils/rules.js';
|
|
3
|
+
export interface FormRulesContext {
|
|
4
|
+
register: (field: {
|
|
5
|
+
focus: () => void;
|
|
6
|
+
validate: () => boolean;
|
|
7
|
+
}) => () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const formRulesKey: InjectionKey<FormRulesContext>;
|
|
10
|
+
export interface FieldRulesState {
|
|
11
|
+
error: Readonly<Ref<string>>;
|
|
12
|
+
input: (value: unknown) => void;
|
|
13
|
+
validate: () => boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function useFieldRules<T>(value: Ref<T>, rules: Ref<Rule<T>[] | undefined>, focus: () => void): FieldRulesState;
|
|
16
|
+
//# sourceMappingURL=useFieldRules.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { computed, inject, onUnmounted, shallowRef, watch } from "vue";
|
|
2
|
+
import { useBrickflow } from "./useBrickflow.js";
|
|
3
|
+
export const formRulesKey = Symbol("brickflow-form-rules");
|
|
4
|
+
export function useFieldRules(value, rules, focus) {
|
|
5
|
+
const config = useBrickflow();
|
|
6
|
+
const form = inject(formRulesKey, void 0);
|
|
7
|
+
const error = shallowRef("");
|
|
8
|
+
const active = shallowRef(false);
|
|
9
|
+
function check(nextValue) {
|
|
10
|
+
for (const rule of rules.value ?? []) {
|
|
11
|
+
const result = rule(nextValue);
|
|
12
|
+
if (result !== true) {
|
|
13
|
+
error.value = result;
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
error.value = "";
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
function validate() {
|
|
21
|
+
active.value = true;
|
|
22
|
+
return check(value.value);
|
|
23
|
+
}
|
|
24
|
+
function input(nextValue) {
|
|
25
|
+
if (active.value) {
|
|
26
|
+
check(nextValue);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
watch(value, () => {
|
|
30
|
+
if (active.value) {
|
|
31
|
+
check(value.value);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
watch([rules, () => config.i18n?.locale.value], () => {
|
|
35
|
+
if (active.value) {
|
|
36
|
+
check(value.value);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const unregister = form?.register({ focus, validate });
|
|
40
|
+
onUnmounted(() => unregister?.());
|
|
41
|
+
return { error: computed(() => error.value), input, validate };
|
|
42
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type Rule<T> = (value: T) => string | true;
|
|
2
|
+
export interface RuleMessages {
|
|
3
|
+
email: () => string;
|
|
4
|
+
length: (params: {
|
|
5
|
+
length: number;
|
|
6
|
+
}) => string;
|
|
7
|
+
maxLength: (params: {
|
|
8
|
+
max: number;
|
|
9
|
+
}) => string;
|
|
10
|
+
minLength: (params: {
|
|
11
|
+
min: number;
|
|
12
|
+
}) => string;
|
|
13
|
+
required: () => string;
|
|
14
|
+
url: () => string;
|
|
15
|
+
}
|
|
16
|
+
export declare const defaultRuleMessages: RuleMessages;
|
|
17
|
+
export declare const rule: {
|
|
18
|
+
email: typeof email;
|
|
19
|
+
length: typeof length;
|
|
20
|
+
maxLength: typeof maxLength;
|
|
21
|
+
minLength: typeof minLength;
|
|
22
|
+
regex: typeof regex;
|
|
23
|
+
required: typeof required;
|
|
24
|
+
url: typeof url;
|
|
25
|
+
};
|
|
26
|
+
declare function email(): Rule<string>;
|
|
27
|
+
declare function length(expectedLength: number): Rule<string>;
|
|
28
|
+
declare function maxLength(max: number): Rule<string>;
|
|
29
|
+
declare function minLength(min: number): Rule<string>;
|
|
30
|
+
declare function regex(pattern: RegExp, message: (() => string) | string): Rule<string>;
|
|
31
|
+
declare function required<T>(): Rule<T>;
|
|
32
|
+
declare function url(): Rule<string>;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=rules.d.ts.map
|