@brickflow/ui 0.0.55 → 0.0.56
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.json +1 -1
- package/dist/runtime/components/Button/state.demo.vue +9 -1
- package/dist/runtime/components/Form/index.vue +5 -0
- package/dist/runtime/components/Form/login.demo.vue +57 -0
- package/dist/runtime/components/Input/index.vue +3 -0
- package/dist/runtime/components/InputNumber/denominator.demo.vue +38 -0
- package/dist/runtime/components/InputNumber/index.vue +109 -7
- package/dist/runtime/components/InputNumber/minmax.demo.vue +39 -0
- package/dist/runtime/components/InputNumber/nullable.demo.vue +25 -0
- package/dist/runtime/components/InputNumber/step.demo.vue +38 -0
- package/package.json +1 -1
- package/dist/runtime/components/InputNumber/basic.demo.vue +0 -33
package/dist/module.json
CHANGED
|
@@ -21,11 +21,19 @@
|
|
|
21
21
|
<div class="flex flex-wrap items-center gap-3">
|
|
22
22
|
<button
|
|
23
23
|
type="button"
|
|
24
|
-
class="cursor-pointer rounded-xl
|
|
24
|
+
class="w-30 cursor-pointer rounded-xl px-3 py-2"
|
|
25
|
+
:class="stateActive ? 'bg-blue-500' : 'bg-blue-800'"
|
|
25
26
|
@click="stateActive = !stateActive"
|
|
26
27
|
>
|
|
27
28
|
{{ stateActive ? 'On' : 'Off' }}
|
|
28
29
|
</button>
|
|
30
|
+
<Button
|
|
31
|
+
:loading="stateActive"
|
|
32
|
+
:icon="firstIconName"
|
|
33
|
+
>
|
|
34
|
+
Loading
|
|
35
|
+
</Button>
|
|
36
|
+
<Button :loading="stateActive">Loading</Button>
|
|
29
37
|
<div class="basis-full">
|
|
30
38
|
<div class="relative flex w-fit flex-col items-start gap-3 pl-13">
|
|
31
39
|
<div class="absolute top-0 left-0 z-0 size-full object-cover pt-7 pl-13">
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
title: 'Email & Password',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
import Button from '../Button/index.vue'
|
|
11
|
+
import Input from '../Input/index.vue'
|
|
12
|
+
import Form from './index.vue'
|
|
13
|
+
|
|
14
|
+
const email = ref('')
|
|
15
|
+
const password = ref('')
|
|
16
|
+
const submittedEmail = ref('')
|
|
17
|
+
|
|
18
|
+
function handleSubmit(): void {
|
|
19
|
+
submittedEmail.value = email.value
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<Form
|
|
25
|
+
class="max-w-sm"
|
|
26
|
+
@submit.prevent="handleSubmit"
|
|
27
|
+
>
|
|
28
|
+
<Input
|
|
29
|
+
:value="email"
|
|
30
|
+
label="Email"
|
|
31
|
+
placeholder="example@brickflow.com"
|
|
32
|
+
type="email"
|
|
33
|
+
autocomplete="email"
|
|
34
|
+
@input="email = $event"
|
|
35
|
+
/>
|
|
36
|
+
<Input
|
|
37
|
+
:value="password"
|
|
38
|
+
label="Password"
|
|
39
|
+
type="password"
|
|
40
|
+
autocomplete="current-password"
|
|
41
|
+
@input="password = $event"
|
|
42
|
+
/>
|
|
43
|
+
<Button
|
|
44
|
+
type="submit"
|
|
45
|
+
class="self-start"
|
|
46
|
+
>
|
|
47
|
+
Sign in
|
|
48
|
+
</Button>
|
|
49
|
+
<p
|
|
50
|
+
v-if="submittedEmail"
|
|
51
|
+
role="status"
|
|
52
|
+
class="text-sm"
|
|
53
|
+
>
|
|
54
|
+
Submitted for {{ submittedEmail }}
|
|
55
|
+
</p>
|
|
56
|
+
</Form>
|
|
57
|
+
</template>
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
const props = withDefaults(
|
|
42
42
|
defineProps<{
|
|
43
43
|
align?: 'center' | 'left' | 'right'
|
|
44
|
+
autocomplete?: string
|
|
44
45
|
autoWidth?: boolean
|
|
45
46
|
description?: string
|
|
46
47
|
disabled?: boolean
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
}>(),
|
|
63
64
|
{
|
|
64
65
|
align: 'left',
|
|
66
|
+
autocomplete: undefined,
|
|
65
67
|
autoWidth: false,
|
|
66
68
|
description: undefined,
|
|
67
69
|
disabled: false,
|
|
@@ -245,6 +247,7 @@
|
|
|
245
247
|
:value="currentValue"
|
|
246
248
|
:type="inputType"
|
|
247
249
|
:inputmode="props.type === 'password' ? undefined : props.type"
|
|
250
|
+
:autocomplete="props.autocomplete"
|
|
248
251
|
:disabled="props.disabled"
|
|
249
252
|
:readonly="props.readonly"
|
|
250
253
|
:placeholder="props.placeholder"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
description: 'Denominator limits decimal places: 0 for integers, 2 for up to two decimal digits.',
|
|
4
|
+
title: 'Denominator',
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script lang="ts" setup>
|
|
9
|
+
import { ref } from 'vue'
|
|
10
|
+
|
|
11
|
+
import InputNumber from './index.vue'
|
|
12
|
+
|
|
13
|
+
const price = ref<number>(8.55)
|
|
14
|
+
const price2 = ref<number>(12.55)
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div class="flex max-w-sm flex-col gap-4">
|
|
19
|
+
<InputNumber
|
|
20
|
+
:value="price"
|
|
21
|
+
:denominator="0"
|
|
22
|
+
label="Denominator: 0"
|
|
23
|
+
:description="`Value: ${price}`"
|
|
24
|
+
:min="-99"
|
|
25
|
+
:max="200"
|
|
26
|
+
@input="price = $event"
|
|
27
|
+
></InputNumber>
|
|
28
|
+
<InputNumber
|
|
29
|
+
:value="price2"
|
|
30
|
+
:denominator="2"
|
|
31
|
+
label="Denominator: 2"
|
|
32
|
+
:description="`Value: ${price2}`"
|
|
33
|
+
:min="-99"
|
|
34
|
+
:max="200"
|
|
35
|
+
@input="price2 = $event"
|
|
36
|
+
></InputNumber>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
@@ -8,37 +8,62 @@
|
|
|
8
8
|
|
|
9
9
|
const props = withDefaults(
|
|
10
10
|
defineProps<{
|
|
11
|
+
align?: InputProps['align']
|
|
12
|
+
autocomplete?: string
|
|
13
|
+
autoWidth?: boolean
|
|
11
14
|
denominator?: number
|
|
12
15
|
description?: string
|
|
13
16
|
disabled?: boolean
|
|
17
|
+
icon?: string
|
|
14
18
|
label?: string
|
|
15
19
|
max?: number
|
|
16
20
|
min?: number
|
|
17
|
-
nullable?: T
|
|
21
|
+
nullable?: boolean & T
|
|
18
22
|
onBlur?: (value: true extends NoInfer<T> ? null | number : number) => void
|
|
23
|
+
onChange?: (value: string, target: HTMLInputElement) => void
|
|
19
24
|
onFocus?: (value: true extends NoInfer<T> ? null | number : number) => void
|
|
25
|
+
onIconClick?: () => void
|
|
20
26
|
onInput?: (value: true extends NoInfer<T> ? null | number : number) => void
|
|
21
27
|
onKeydown?: (event: KeyboardEvent) => void
|
|
28
|
+
onTrailingIconClick?: () => void
|
|
29
|
+
placeholder?: string
|
|
30
|
+
readonly?: boolean
|
|
31
|
+
size?: InputProps['size']
|
|
22
32
|
step?: number
|
|
33
|
+
trailingIcon?: string
|
|
23
34
|
value: true extends NoInfer<T> ? null | number : number
|
|
35
|
+
variant?: InputProps['variant']
|
|
24
36
|
}>(),
|
|
25
37
|
{
|
|
38
|
+
align: undefined,
|
|
39
|
+
autocomplete: undefined,
|
|
40
|
+
autoWidth: false,
|
|
26
41
|
denominator: 0,
|
|
27
42
|
description: undefined,
|
|
28
43
|
disabled: false,
|
|
44
|
+
icon: undefined,
|
|
29
45
|
label: undefined,
|
|
30
46
|
max: undefined,
|
|
31
47
|
min: undefined,
|
|
32
48
|
nullable: undefined,
|
|
33
49
|
onBlur: undefined,
|
|
50
|
+
onChange: undefined,
|
|
34
51
|
onFocus: undefined,
|
|
52
|
+
onIconClick: undefined,
|
|
35
53
|
onInput: undefined,
|
|
36
54
|
onKeydown: undefined,
|
|
37
|
-
|
|
55
|
+
onTrailingIconClick: undefined,
|
|
56
|
+
placeholder: undefined,
|
|
57
|
+
readonly: false,
|
|
58
|
+
size: undefined,
|
|
38
59
|
step: 1,
|
|
60
|
+
trailingIcon: undefined,
|
|
61
|
+
variant: undefined,
|
|
39
62
|
},
|
|
40
63
|
)
|
|
41
64
|
|
|
65
|
+
type InputProps = InstanceType<typeof Input>['$props']
|
|
66
|
+
|
|
42
67
|
const inputValue = shallowRef(prepareValue(props.value))
|
|
43
68
|
|
|
44
69
|
watch(
|
|
@@ -52,11 +77,25 @@
|
|
|
52
77
|
if (!target) {
|
|
53
78
|
return
|
|
54
79
|
}
|
|
80
|
+
if (props.nullable && target.value === '') {
|
|
81
|
+
inputValue.value = ''
|
|
82
|
+
props.onBlur?.(null as typeof props.value)
|
|
83
|
+
return
|
|
84
|
+
}
|
|
55
85
|
const nextValue = prepareValue(props.value)
|
|
56
86
|
target.value = nextValue
|
|
57
87
|
inputValue.value = nextValue
|
|
58
88
|
props.onBlur?.(props.value)
|
|
59
89
|
}
|
|
90
|
+
|
|
91
|
+
function handleChange(value: string, target: HTMLInputElement): void {
|
|
92
|
+
props.onChange?.(value, target)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function handleFocus(): void {
|
|
96
|
+
props.onFocus?.(props.value)
|
|
97
|
+
}
|
|
98
|
+
|
|
60
99
|
const allowedRegexp = /^-?\d*(?:\.\d*)?$/
|
|
61
100
|
const validNumberRegexp = /^-?(?:\d+(?:\.\d+)?|\.\d+)$/
|
|
62
101
|
|
|
@@ -64,19 +103,36 @@
|
|
|
64
103
|
if (!target) {
|
|
65
104
|
return
|
|
66
105
|
}
|
|
67
|
-
|
|
106
|
+
let nextValue = rawValue.replace(/,/, '.')
|
|
68
107
|
target.value = nextValue
|
|
69
108
|
|
|
70
109
|
if (isEmpty(nextValue)) {
|
|
110
|
+
if (props.nullable) {
|
|
111
|
+
inputValue.value = ''
|
|
112
|
+
props.onInput?.(null as typeof props.value)
|
|
113
|
+
}
|
|
71
114
|
return
|
|
72
115
|
}
|
|
73
116
|
if (!allowedRegexp.test(nextValue)) {
|
|
74
117
|
target.value = inputValue.value
|
|
75
118
|
return
|
|
76
119
|
}
|
|
77
|
-
|
|
120
|
+
const fraction = nextValue.split('.')[1]
|
|
121
|
+
if (fraction !== undefined && (props.denominator === 0 || fraction.length > props.denominator)) {
|
|
122
|
+
nextValue =
|
|
123
|
+
props.denominator === 0
|
|
124
|
+
? nextValue.slice(0, nextValue.indexOf('.'))
|
|
125
|
+
: nextValue.slice(0, nextValue.indexOf('.') + props.denominator + 1)
|
|
126
|
+
target.value = nextValue
|
|
127
|
+
}
|
|
128
|
+
if (isEmpty(nextValue) || !validNumberRegexp.test(nextValue)) {
|
|
129
|
+
if (isEmpty(nextValue) && props.nullable) {
|
|
130
|
+
inputValue.value = ''
|
|
131
|
+
props.onInput?.(null as typeof props.value)
|
|
132
|
+
}
|
|
78
133
|
return
|
|
79
134
|
}
|
|
135
|
+
|
|
80
136
|
let nextValueNumber = Number(nextValue)
|
|
81
137
|
if (!isEmpty(props.min) && nextValueNumber < props.min) {
|
|
82
138
|
target.value = prepareValue(props.min)
|
|
@@ -86,8 +142,41 @@
|
|
|
86
142
|
target.value = prepareValue(props.max)
|
|
87
143
|
nextValueNumber = props.max
|
|
88
144
|
}
|
|
145
|
+
|
|
89
146
|
props.onInput?.(nextValueNumber)
|
|
90
147
|
}
|
|
148
|
+
|
|
149
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
150
|
+
props.onKeydown?.(event)
|
|
151
|
+
if (event.defaultPrevented || (event.key !== 'ArrowUp' && event.key !== 'ArrowDown')) {
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
const target = event.target
|
|
155
|
+
if (!(target instanceof HTMLInputElement) || target.disabled || target.readOnly) {
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
if (!Number.isFinite(props.step) || props.step <= 0) {
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
event.preventDefault()
|
|
163
|
+
const parsedValue = Number(target.value)
|
|
164
|
+
const currentValue = target.value !== '' && Number.isFinite(parsedValue) ? parsedValue : (props.value ?? 0)
|
|
165
|
+
const direction = event.key === 'ArrowUp' ? 1 : -1
|
|
166
|
+
const step = props.step * (event.shiftKey ? 2 : 1)
|
|
167
|
+
const precision = Math.max(0, Math.min(100, Math.trunc(props.denominator)))
|
|
168
|
+
let nextValue = Number((currentValue + direction * step).toFixed(precision))
|
|
169
|
+
if (!isEmpty(props.min) && nextValue < props.min) {
|
|
170
|
+
nextValue = props.min
|
|
171
|
+
}
|
|
172
|
+
if (!isEmpty(props.max) && nextValue > props.max) {
|
|
173
|
+
nextValue = props.max
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
target.value = prepareValue(nextValue)
|
|
177
|
+
props.onInput?.(nextValue)
|
|
178
|
+
}
|
|
179
|
+
|
|
91
180
|
function prepareValue(value: typeof props.value): string {
|
|
92
181
|
return value === null ? '' : String(value)
|
|
93
182
|
}
|
|
@@ -97,15 +186,28 @@
|
|
|
97
186
|
<Input
|
|
98
187
|
v-bind="{
|
|
99
188
|
...$attrs,
|
|
100
|
-
// onBlur: handleBlur,
|
|
101
|
-
// onKeydown: handleKeydown,
|
|
102
189
|
}"
|
|
103
190
|
:value="inputValue"
|
|
191
|
+
:align="props.align"
|
|
192
|
+
:autocomplete="props.autocomplete"
|
|
193
|
+
:autoWidth="props.autoWidth"
|
|
104
194
|
:label="props.label"
|
|
105
195
|
:description="props.description"
|
|
106
|
-
|
|
196
|
+
:disabled="props.disabled"
|
|
197
|
+
:icon="props.icon"
|
|
198
|
+
:placeholder="props.placeholder"
|
|
199
|
+
:readonly="props.readonly"
|
|
200
|
+
:size="props.size"
|
|
201
|
+
:trailingIcon="props.trailingIcon"
|
|
202
|
+
:type="props.denominator === 0 ? 'numeric' : 'decimal'"
|
|
203
|
+
:variant="props.variant"
|
|
107
204
|
@input="handleInput"
|
|
205
|
+
@change="handleChange"
|
|
108
206
|
@blur="handleBlur"
|
|
207
|
+
@focus="handleFocus"
|
|
208
|
+
@iconClick="props.onIconClick?.()"
|
|
209
|
+
@keydown="handleKeydown"
|
|
210
|
+
@trailingIconClick="props.onTrailingIconClick?.()"
|
|
109
211
|
>
|
|
110
212
|
<template
|
|
111
213
|
v-if="$slots.label"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
title: 'Min & Max',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
import InputNumber from './index.vue'
|
|
11
|
+
|
|
12
|
+
const price1 = ref<number>(25)
|
|
13
|
+
const price2 = ref<number>(-10)
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<div class="flex max-w-sm flex-col gap-4">
|
|
18
|
+
<InputNumber
|
|
19
|
+
:value="price1"
|
|
20
|
+
label="Min: 0 — Max: 200"
|
|
21
|
+
:description="`Value: ${price1}`"
|
|
22
|
+
:min="0"
|
|
23
|
+
:max="200"
|
|
24
|
+
@input="price1 = $event"
|
|
25
|
+
>
|
|
26
|
+
<template #leading><span class="text-green-400">$</span></template>
|
|
27
|
+
</InputNumber>
|
|
28
|
+
<InputNumber
|
|
29
|
+
:value="price2"
|
|
30
|
+
label="Min: -100 — Max: 199"
|
|
31
|
+
:description="`Value: ${price2}`"
|
|
32
|
+
:min="-100"
|
|
33
|
+
:max="199"
|
|
34
|
+
@input="price2 = $event"
|
|
35
|
+
>
|
|
36
|
+
<template #leading><span class="text-green-400">$</span></template>
|
|
37
|
+
</InputNumber>
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
title: 'Nullable',
|
|
4
|
+
}
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
|
|
10
|
+
import InputNumber from './index.vue'
|
|
11
|
+
|
|
12
|
+
const value = ref<null | number>(null)
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div class="flex max-w-sm flex-col gap-4">
|
|
17
|
+
<InputNumber
|
|
18
|
+
:value="value"
|
|
19
|
+
nullable
|
|
20
|
+
label="Nullable (clear to set null)"
|
|
21
|
+
:description="`Value: ${value === null ? 'null' : value}`"
|
|
22
|
+
@input="value = $event"
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export const uiDemo = {
|
|
3
|
+
description: 'Step sets the change per arrow key press. Hold Shift to double it.',
|
|
4
|
+
title: 'Step',
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script lang="ts" setup>
|
|
9
|
+
import { ref } from 'vue'
|
|
10
|
+
|
|
11
|
+
import InputNumber from './index.vue'
|
|
12
|
+
|
|
13
|
+
const quantity = ref<number>(2)
|
|
14
|
+
const price = ref<number>(25)
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div class="flex max-w-sm flex-col gap-4">
|
|
19
|
+
<InputNumber
|
|
20
|
+
:value="quantity"
|
|
21
|
+
:step="1"
|
|
22
|
+
label="Step: 1"
|
|
23
|
+
:min="0"
|
|
24
|
+
:max="200"
|
|
25
|
+
:description="`Value: ${quantity}`"
|
|
26
|
+
@input="quantity = $event"
|
|
27
|
+
/>
|
|
28
|
+
<InputNumber
|
|
29
|
+
:value="price"
|
|
30
|
+
:step="5"
|
|
31
|
+
label="Step: 5"
|
|
32
|
+
:min="-10"
|
|
33
|
+
:max="200"
|
|
34
|
+
:description="`Value: ${price}`"
|
|
35
|
+
@input="price = $event"
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
package/package.json
CHANGED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
export const uiDemo = {
|
|
3
|
-
title: 'Number & Bounds',
|
|
4
|
-
}
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<script lang="ts" setup>
|
|
8
|
-
import { ref } from 'vue'
|
|
9
|
-
|
|
10
|
-
import InputNumber from './index.vue'
|
|
11
|
-
|
|
12
|
-
const price = ref<number>(25)
|
|
13
|
-
function handleInput(event: number): void {
|
|
14
|
-
price.value = event
|
|
15
|
-
}
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
<template>
|
|
19
|
-
<div class="flex max-w-sm flex-col gap-4">
|
|
20
|
-
<InputNumber
|
|
21
|
-
:value="price"
|
|
22
|
-
label="Price"
|
|
23
|
-
description="Min: 0 — Max: 200"
|
|
24
|
-
:min="0"
|
|
25
|
-
:max="200"
|
|
26
|
-
:step="5"
|
|
27
|
-
@input="handleInput"
|
|
28
|
-
>
|
|
29
|
-
<template #leading><span class="text-green-400">$</span></template>
|
|
30
|
-
</InputNumber>
|
|
31
|
-
<p class="text-gray-500">Value: {{ price }}</p>
|
|
32
|
-
</div>
|
|
33
|
-
</template>
|