@brickflow/ui 0.0.54 → 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 +21 -19
- package/dist/runtime/components/Input/size.demo.vue +5 -0
- package/dist/runtime/components/InputNumber/denominator.demo.vue +38 -0
- package/dist/runtime/components/InputNumber/index.vue +174 -57
- 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/dist/runtime/nuxt-components.d.ts +7 -0
- package/dist/runtime/utils/checkup.d.ts +32 -0
- package/dist/runtime/utils/checkup.js +67 -0
- package/package.json +1 -1
- package/dist/runtime/components/InputNumber/basic.demo.vue +0 -37
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,17 +41,18 @@
|
|
|
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
|
|
47
48
|
icon?: string
|
|
48
49
|
label?: string
|
|
49
|
-
onBlur?: (value:
|
|
50
|
-
|
|
50
|
+
onBlur?: (value: string, target: HTMLInputElement) => void
|
|
51
|
+
onChange?: (value: string, target: HTMLInputElement) => void
|
|
52
|
+
onFocus?: (value: string, target: HTMLInputElement | null) => void
|
|
51
53
|
onIconClick?: () => void
|
|
54
|
+
onInput?: (value: string, target: HTMLInputElement | null) => void
|
|
52
55
|
onTrailingIconClick?: () => void
|
|
53
|
-
onValueChange?: (value: null | string, target: HTMLInputElement) => void
|
|
54
|
-
onValueInput?: (value: null | string, target: HTMLInputElement | null) => void
|
|
55
56
|
placeholder?: string
|
|
56
57
|
readonly?: boolean
|
|
57
58
|
size?: InputSize
|
|
@@ -62,17 +63,18 @@
|
|
|
62
63
|
}>(),
|
|
63
64
|
{
|
|
64
65
|
align: 'left',
|
|
66
|
+
autocomplete: undefined,
|
|
65
67
|
autoWidth: false,
|
|
66
68
|
description: undefined,
|
|
67
69
|
disabled: false,
|
|
68
70
|
icon: undefined,
|
|
69
71
|
label: undefined,
|
|
70
72
|
onBlur: undefined,
|
|
73
|
+
onChange: undefined,
|
|
71
74
|
onFocus: undefined,
|
|
72
75
|
onIconClick: undefined,
|
|
76
|
+
onInput: undefined,
|
|
73
77
|
onTrailingIconClick: undefined,
|
|
74
|
-
onValueChange: undefined,
|
|
75
|
-
onValueInput: undefined,
|
|
76
78
|
placeholder: '',
|
|
77
79
|
readonly: false,
|
|
78
80
|
size: 'md',
|
|
@@ -83,8 +85,7 @@
|
|
|
83
85
|
},
|
|
84
86
|
)
|
|
85
87
|
|
|
86
|
-
const model = shallowRef
|
|
87
|
-
// const model = defineModel<null | number | string>({ default: '' })
|
|
88
|
+
const model = shallowRef('')
|
|
88
89
|
|
|
89
90
|
const attrs = useAttrs()
|
|
90
91
|
const slots = useSlots()
|
|
@@ -104,7 +105,7 @@
|
|
|
104
105
|
|
|
105
106
|
const fieldId = computed(() => String(attrs.id ?? generatedId))
|
|
106
107
|
const descriptionId = computed(() => `${fieldId.value}-description`)
|
|
107
|
-
const currentValue = computed(() => (props.value === undefined ? model.value : props.value))
|
|
108
|
+
const currentValue = computed(() => (props.value === undefined ? model.value : (props.value ?? '')))
|
|
108
109
|
const widthMeasureText = computed(() => currentValue.value || props.placeholder || ' ')
|
|
109
110
|
const isEmptyField = computed(() => {
|
|
110
111
|
return Boolean(currentValue.value)
|
|
@@ -177,23 +178,23 @@
|
|
|
177
178
|
props.onBlur?.(currentValue.value, event.target as HTMLInputElement)
|
|
178
179
|
}
|
|
179
180
|
|
|
181
|
+
function handleChange(event: Event): void {
|
|
182
|
+
const target = event.target as HTMLInputElement
|
|
183
|
+
props.onChange?.(target.value, target)
|
|
184
|
+
}
|
|
185
|
+
|
|
180
186
|
function handleFocus(event: FocusEvent): void {
|
|
181
187
|
hasFocus.value = true
|
|
182
188
|
props.onFocus?.(currentValue.value, event.target as HTMLInputElement)
|
|
183
189
|
}
|
|
184
190
|
|
|
185
|
-
function
|
|
186
|
-
const target = event.target as HTMLInputElement
|
|
187
|
-
props.onValueChange?.(target.value, target)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function onInput(event: Event): void {
|
|
191
|
+
function handleInput(event: Event): void {
|
|
191
192
|
setValue((event.target as HTMLInputElement).value)
|
|
192
193
|
}
|
|
193
194
|
|
|
194
|
-
function setValue(value:
|
|
195
|
+
function setValue(value: string): void {
|
|
195
196
|
model.value = value
|
|
196
|
-
props.
|
|
197
|
+
props.onInput?.(value, inputRef.value)
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
defineExpose({ clear, fieldId, focus, hasFocus, inputRef })
|
|
@@ -246,14 +247,15 @@
|
|
|
246
247
|
:value="currentValue"
|
|
247
248
|
:type="inputType"
|
|
248
249
|
:inputmode="props.type === 'password' ? undefined : props.type"
|
|
250
|
+
:autocomplete="props.autocomplete"
|
|
249
251
|
:disabled="props.disabled"
|
|
250
252
|
:readonly="props.readonly"
|
|
251
253
|
:placeholder="props.placeholder"
|
|
252
254
|
:aria-describedby="hasDescription ? descriptionId : undefined"
|
|
253
255
|
:class="fieldClasses"
|
|
254
256
|
:style="fieldStyle"
|
|
255
|
-
@input="
|
|
256
|
-
@change="
|
|
257
|
+
@input="handleInput"
|
|
258
|
+
@change="handleChange"
|
|
257
259
|
@focus="handleFocus"
|
|
258
260
|
@blur="handleBlur"
|
|
259
261
|
/>
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
7
|
<script lang="ts" setup>
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
|
|
8
10
|
import { firstIconName, uiConfig } from '#brickflow-ui'
|
|
9
11
|
|
|
10
12
|
import Button from '../Button/index.vue'
|
|
@@ -15,6 +17,7 @@
|
|
|
15
17
|
function copy(): void {
|
|
16
18
|
console.log('Click')
|
|
17
19
|
}
|
|
20
|
+
const text = ref('')
|
|
18
21
|
</script>
|
|
19
22
|
|
|
20
23
|
<template>
|
|
@@ -26,6 +29,7 @@
|
|
|
26
29
|
style="grid-template-columns: minmax(0, 24rem) max-content; grid-template-rows: repeat(3, auto)"
|
|
27
30
|
>
|
|
28
31
|
<Input
|
|
32
|
+
:value="text"
|
|
29
33
|
:size="size"
|
|
30
34
|
:icon="firstIconName"
|
|
31
35
|
:trailingIcon="firstIconName"
|
|
@@ -34,6 +38,7 @@
|
|
|
34
38
|
:label="`Size: ${size}`"
|
|
35
39
|
class="row-span-3 grid! grid-rows-subgrid"
|
|
36
40
|
@iconClick="copy"
|
|
41
|
+
@input="text = $event"
|
|
37
42
|
/>
|
|
38
43
|
<Button
|
|
39
44
|
:size="size"
|
|
@@ -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>
|
|
@@ -1,79 +1,184 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import {
|
|
1
|
+
<script setup lang="ts" generic="T extends boolean = false">
|
|
2
|
+
import { shallowRef, watch } from 'vue'
|
|
3
3
|
|
|
4
|
+
import { isEmpty } from '../../utils/checkup.js'
|
|
4
5
|
import Input from '../Input/index.vue'
|
|
5
6
|
|
|
6
7
|
defineOptions({ inheritAttrs: false })
|
|
7
8
|
|
|
8
9
|
const props = withDefaults(
|
|
9
10
|
defineProps<{
|
|
11
|
+
align?: InputProps['align']
|
|
12
|
+
autocomplete?: string
|
|
13
|
+
autoWidth?: boolean
|
|
14
|
+
denominator?: number
|
|
15
|
+
description?: string
|
|
16
|
+
disabled?: boolean
|
|
17
|
+
icon?: string
|
|
18
|
+
label?: string
|
|
10
19
|
max?: number
|
|
11
20
|
min?: number
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
nullable?: boolean & T
|
|
22
|
+
onBlur?: (value: true extends NoInfer<T> ? null | number : number) => void
|
|
23
|
+
onChange?: (value: string, target: HTMLInputElement) => void
|
|
24
|
+
onFocus?: (value: true extends NoInfer<T> ? null | number : number) => void
|
|
25
|
+
onIconClick?: () => void
|
|
26
|
+
onInput?: (value: true extends NoInfer<T> ? null | number : number) => void
|
|
27
|
+
onKeydown?: (event: KeyboardEvent) => void
|
|
28
|
+
onTrailingIconClick?: () => void
|
|
29
|
+
placeholder?: string
|
|
30
|
+
readonly?: boolean
|
|
31
|
+
size?: InputProps['size']
|
|
14
32
|
step?: number
|
|
15
|
-
|
|
33
|
+
trailingIcon?: string
|
|
34
|
+
value: true extends NoInfer<T> ? null | number : number
|
|
35
|
+
variant?: InputProps['variant']
|
|
16
36
|
}>(),
|
|
17
37
|
{
|
|
38
|
+
align: undefined,
|
|
39
|
+
autocomplete: undefined,
|
|
40
|
+
autoWidth: false,
|
|
41
|
+
denominator: 0,
|
|
42
|
+
description: undefined,
|
|
43
|
+
disabled: false,
|
|
44
|
+
icon: undefined,
|
|
45
|
+
label: undefined,
|
|
18
46
|
max: undefined,
|
|
19
47
|
min: undefined,
|
|
20
|
-
|
|
21
|
-
|
|
48
|
+
nullable: undefined,
|
|
49
|
+
onBlur: undefined,
|
|
50
|
+
onChange: undefined,
|
|
51
|
+
onFocus: undefined,
|
|
52
|
+
onIconClick: undefined,
|
|
53
|
+
onInput: undefined,
|
|
54
|
+
onKeydown: undefined,
|
|
55
|
+
onTrailingIconClick: undefined,
|
|
56
|
+
placeholder: undefined,
|
|
57
|
+
readonly: false,
|
|
58
|
+
size: undefined,
|
|
22
59
|
step: 1,
|
|
23
|
-
|
|
60
|
+
trailingIcon: undefined,
|
|
61
|
+
variant: undefined,
|
|
24
62
|
},
|
|
25
63
|
)
|
|
26
64
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
65
|
+
type InputProps = InstanceType<typeof Input>['$props']
|
|
66
|
+
|
|
67
|
+
const inputValue = shallowRef(prepareValue(props.value))
|
|
68
|
+
|
|
69
|
+
watch(
|
|
70
|
+
() => props.value,
|
|
71
|
+
() => {
|
|
72
|
+
inputValue.value = prepareValue(props.value)
|
|
73
|
+
},
|
|
32
74
|
)
|
|
33
75
|
|
|
34
|
-
function
|
|
35
|
-
|
|
76
|
+
function handleBlur(value: string, target: HTMLInputElement): void {
|
|
77
|
+
if (!target) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
if (props.nullable && target.value === '') {
|
|
81
|
+
inputValue.value = ''
|
|
82
|
+
props.onBlur?.(null as typeof props.value)
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
const nextValue = prepareValue(props.value)
|
|
86
|
+
target.value = nextValue
|
|
87
|
+
inputValue.value = nextValue
|
|
88
|
+
props.onBlur?.(props.value)
|
|
36
89
|
}
|
|
37
90
|
|
|
38
|
-
function
|
|
39
|
-
|
|
91
|
+
function handleChange(value: string, target: HTMLInputElement): void {
|
|
92
|
+
props.onChange?.(value, target)
|
|
40
93
|
}
|
|
41
94
|
|
|
42
|
-
function
|
|
43
|
-
|
|
95
|
+
function handleFocus(): void {
|
|
96
|
+
props.onFocus?.(props.value)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const allowedRegexp = /^-?\d*(?:\.\d*)?$/
|
|
100
|
+
const validNumberRegexp = /^-?(?:\d+(?:\.\d+)?|\.\d+)$/
|
|
101
|
+
|
|
102
|
+
function handleInput(rawValue: string, target: HTMLInputElement | null): void {
|
|
103
|
+
if (!target) {
|
|
44
104
|
return
|
|
45
105
|
}
|
|
106
|
+
let nextValue = rawValue.replace(/,/, '.')
|
|
107
|
+
target.value = nextValue
|
|
46
108
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
109
|
+
if (isEmpty(nextValue)) {
|
|
110
|
+
if (props.nullable) {
|
|
111
|
+
inputValue.value = ''
|
|
112
|
+
props.onInput?.(null as typeof props.value)
|
|
113
|
+
}
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
if (!allowedRegexp.test(nextValue)) {
|
|
117
|
+
target.value = inputValue.value
|
|
118
|
+
return
|
|
119
|
+
}
|
|
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
|
+
}
|
|
133
|
+
return
|
|
134
|
+
}
|
|
54
135
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
props.
|
|
61
|
-
|
|
136
|
+
let nextValueNumber = Number(nextValue)
|
|
137
|
+
if (!isEmpty(props.min) && nextValueNumber < props.min) {
|
|
138
|
+
target.value = prepareValue(props.min)
|
|
139
|
+
nextValueNumber = props.min
|
|
140
|
+
}
|
|
141
|
+
if (!isEmpty(props.max) && nextValueNumber > props.max) {
|
|
142
|
+
target.value = prepareValue(props.max)
|
|
143
|
+
nextValueNumber = props.max
|
|
144
|
+
}
|
|
62
145
|
|
|
63
|
-
|
|
64
|
-
rawValue.value = value ?? ''
|
|
65
|
-
const number = toNumber(value)
|
|
66
|
-
model.value = number
|
|
67
|
-
props.onValueInput?.(number, target)
|
|
146
|
+
props.onInput?.(nextValueNumber)
|
|
68
147
|
}
|
|
69
148
|
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
73
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
|
+
}
|
|
74
179
|
|
|
75
|
-
|
|
76
|
-
return
|
|
180
|
+
function prepareValue(value: typeof props.value): string {
|
|
181
|
+
return value === null ? '' : String(value)
|
|
77
182
|
}
|
|
78
183
|
</script>
|
|
79
184
|
|
|
@@ -81,16 +186,28 @@
|
|
|
81
186
|
<Input
|
|
82
187
|
v-bind="{
|
|
83
188
|
...$attrs,
|
|
84
|
-
onValueInput: handleValueInput,
|
|
85
|
-
onValueChange: handleValueChange,
|
|
86
|
-
onBlur: handleBlur,
|
|
87
|
-
onKeydown: handleKeydown,
|
|
88
189
|
}"
|
|
89
190
|
:value="inputValue"
|
|
90
|
-
|
|
91
|
-
:
|
|
92
|
-
:
|
|
93
|
-
:
|
|
191
|
+
:align="props.align"
|
|
192
|
+
:autocomplete="props.autocomplete"
|
|
193
|
+
:autoWidth="props.autoWidth"
|
|
194
|
+
:label="props.label"
|
|
195
|
+
:description="props.description"
|
|
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"
|
|
204
|
+
@input="handleInput"
|
|
205
|
+
@change="handleChange"
|
|
206
|
+
@blur="handleBlur"
|
|
207
|
+
@focus="handleFocus"
|
|
208
|
+
@iconClick="props.onIconClick?.()"
|
|
209
|
+
@keydown="handleKeydown"
|
|
210
|
+
@trailingIconClick="props.onTrailingIconClick?.()"
|
|
94
211
|
>
|
|
95
212
|
<template
|
|
96
213
|
v-if="$slots.label"
|
|
@@ -99,16 +216,16 @@
|
|
|
99
216
|
<slot name="label" />
|
|
100
217
|
</template>
|
|
101
218
|
<template
|
|
102
|
-
v-if="$slots.
|
|
103
|
-
#
|
|
219
|
+
v-if="$slots.leading"
|
|
220
|
+
#leading
|
|
104
221
|
>
|
|
105
|
-
<slot name="
|
|
222
|
+
<slot name="leading" />
|
|
106
223
|
</template>
|
|
107
224
|
<template
|
|
108
|
-
v-if="$slots.
|
|
109
|
-
#
|
|
225
|
+
v-if="$slots.trailing"
|
|
226
|
+
#trailing
|
|
110
227
|
>
|
|
111
|
-
<slot name="
|
|
228
|
+
<slot name="trailing" />
|
|
112
229
|
</template>
|
|
113
230
|
<template
|
|
114
231
|
v-if="$slots.error"
|
|
@@ -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>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @returns isEmpty('') => true
|
|
3
|
+
* @returns isEmpty([]) => true
|
|
4
|
+
* @returns isEmpty([1]) => false
|
|
5
|
+
* @returns isEmpty([null]) => false
|
|
6
|
+
* @returns isEmpty({}) => true
|
|
7
|
+
* @returns isEmpty({ key: '' }) => false
|
|
8
|
+
* @returns isEmpty(undefined) => true
|
|
9
|
+
* @returns isEmpty(null) => true
|
|
10
|
+
* @returns isEmpty(new Date()) => false
|
|
11
|
+
* @returns isEmpty(new Date('Invalid Date')) => false
|
|
12
|
+
* @returns isEmpty(true) => false
|
|
13
|
+
* @returns isEmpty(false) => false
|
|
14
|
+
* @returns isEmpty(0), 0, false
|
|
15
|
+
* @returns isEmpty(() => undefined) => false
|
|
16
|
+
* @returns isEmpty(Infinity) => false
|
|
17
|
+
* @returns isEmpty(-Infinity) => false
|
|
18
|
+
* @returns isEmpty(new Promise((resolve) => resolve(''))) => true
|
|
19
|
+
*/
|
|
20
|
+
export declare function isEmpty<T>(value: T): value is Extract<T, [] | null | undefined>;
|
|
21
|
+
export declare function isFloat(value: number): boolean;
|
|
22
|
+
export declare function isNumber(value: unknown): value is number;
|
|
23
|
+
export declare function isPromise<T>(value: T): value is T extends Promise<unknown> ? T : never;
|
|
24
|
+
export declare function isString(value: unknown): value is string;
|
|
25
|
+
export declare function isValidDate(date: Date | number | string): boolean;
|
|
26
|
+
export declare const isNonNullable: <T>(value: T) => value is NonNullable<T>;
|
|
27
|
+
export declare function isElement<T>(value: T): value is Extract<T, Element>;
|
|
28
|
+
export declare function isExternalLink(link: string): boolean;
|
|
29
|
+
export declare function isFormData(value?: FormData | Record<string, unknown> | unknown): value is FormData;
|
|
30
|
+
export declare function isFunction(value: unknown): value is (...args: unknown[]) => unknown;
|
|
31
|
+
export declare function isTouchDevice(): boolean;
|
|
32
|
+
//# sourceMappingURL=checkup.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export function isEmpty(value) {
|
|
2
|
+
if (typeof value === "boolean" || typeof value === "function") {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
if (typeof value === "undefined" || value === null || typeof value === "string" && value.length === 0) {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (isDate(value)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return Object.keys(value).length === 0;
|
|
18
|
+
}
|
|
19
|
+
export function isFloat(value) {
|
|
20
|
+
return Number(value) === value && value % 1 !== 0;
|
|
21
|
+
}
|
|
22
|
+
export function isNumber(value) {
|
|
23
|
+
return typeof value === "number";
|
|
24
|
+
}
|
|
25
|
+
export function isPromise(value) {
|
|
26
|
+
return Boolean(value) && (typeof value === "object" || typeof value === "function") && typeof value?.then === "function";
|
|
27
|
+
}
|
|
28
|
+
export function isString(value) {
|
|
29
|
+
return typeof value === "string";
|
|
30
|
+
}
|
|
31
|
+
export function isValidDate(date) {
|
|
32
|
+
if (typeof date === "string") {
|
|
33
|
+
return Boolean(Date.parse(date));
|
|
34
|
+
}
|
|
35
|
+
if (isNaN(date)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return typeof date === "number" || date instanceof Date;
|
|
39
|
+
}
|
|
40
|
+
function isDate(value) {
|
|
41
|
+
if (Object.prototype.toString.call(value) === "[object Date]") {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
export const isNonNullable = (value) => {
|
|
47
|
+
return value !== void 0 && value !== null;
|
|
48
|
+
};
|
|
49
|
+
export function isElement(value) {
|
|
50
|
+
return value instanceof Element;
|
|
51
|
+
}
|
|
52
|
+
export function isExternalLink(link) {
|
|
53
|
+
const EXTERNAL_LINK_REGEX = /^(?:https?:\/\/|mailto:|tel:)/;
|
|
54
|
+
return EXTERNAL_LINK_REGEX.test(link);
|
|
55
|
+
}
|
|
56
|
+
export function isFormData(value) {
|
|
57
|
+
if (!value) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return typeof value === "object" && typeof value.append === "function" && typeof value.has === "function";
|
|
61
|
+
}
|
|
62
|
+
export function isFunction(value) {
|
|
63
|
+
return typeof value === "function";
|
|
64
|
+
}
|
|
65
|
+
export function isTouchDevice() {
|
|
66
|
+
return "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0;
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,37 +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 quantity = ref<null | number>(null)
|
|
13
|
-
const price = ref<null | number>(25)
|
|
14
|
-
</script>
|
|
15
|
-
|
|
16
|
-
<template>
|
|
17
|
-
<div class="flex max-w-sm flex-col gap-4">
|
|
18
|
-
<InputNumber
|
|
19
|
-
v-model="quantity"
|
|
20
|
-
label="Quantity"
|
|
21
|
-
description="Use ↑ and ↓ to change by one."
|
|
22
|
-
:min="0"
|
|
23
|
-
:max="20"
|
|
24
|
-
block
|
|
25
|
-
/>
|
|
26
|
-
<InputNumber
|
|
27
|
-
v-model="price"
|
|
28
|
-
label="Price"
|
|
29
|
-
description="Changes in steps of five."
|
|
30
|
-
:min="0"
|
|
31
|
-
:max="100"
|
|
32
|
-
:step="5"
|
|
33
|
-
block
|
|
34
|
-
/>
|
|
35
|
-
<p class="text-sm text-gray-400">Quantity: {{ quantity ?? '—' }} · Price: {{ price ?? '—' }}</p>
|
|
36
|
-
</div>
|
|
37
|
-
</template>
|