@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 CHANGED
@@ -4,7 +4,7 @@
4
4
  },
5
5
  "configKey": "brickflowUi",
6
6
  "name": "@brickflow/ui",
7
- "version": "0.0.54",
7
+ "version": "0.0.56",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.3",
10
10
  "unbuild": "3.6.1"
@@ -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 bg-blue-500 px-3 py-2"
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,5 @@
1
+ <template>
2
+ <form class="flex flex-col gap-4">
3
+ <slot />
4
+ </form>
5
+ </template>
@@ -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: null | string, target: HTMLInputElement) => void
50
- onFocus?: (value: null | string, target: HTMLInputElement | null) => void
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<null | string>('')
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 onChange(event: Event): void {
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: null | string): void {
195
+ function setValue(value: string): void {
195
196
  model.value = value
196
- props.onValueInput?.(value, inputRef.value)
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="onInput"
256
- @change="onChange"
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 { computed, ref } from 'vue'
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
- onValueChange?: (value: null | number, target: HTMLInputElement) => void
13
- onValueInput?: (value: null | number, target: HTMLInputElement | null) => void
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
- value?: null | number
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
- onValueChange: undefined,
21
- onValueInput: undefined,
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
- value: undefined,
60
+ trailingIcon: undefined,
61
+ variant: undefined,
24
62
  },
25
63
  )
26
64
 
27
- const model = defineModel<null | number>({ default: null })
28
- const rawValue = ref<null | string>(null)
29
- const currentValue = computed(() => (props.value === undefined ? model.value : props.value))
30
- const inputValue = computed(
31
- () => rawValue.value ?? (currentValue.value === null ? '' : String(currentValue.value)),
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 clamp(value: number): number {
35
- return Math.min(props.max ?? Infinity, Math.max(props.min ?? -Infinity, value))
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 handleBlur(): void {
39
- rawValue.value = null
91
+ function handleChange(value: string, target: HTMLInputElement): void {
92
+ props.onChange?.(value, target)
40
93
  }
41
94
 
42
- function handleKeydown(event: KeyboardEvent): void {
43
- if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') {
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
- event.preventDefault()
48
- const next = (currentValue.value ?? 0) + (event.key === 'ArrowUp' ? props.step : -props.step)
49
- const value = clamp(next)
50
- rawValue.value = String(value)
51
- model.value = value
52
- props.onValueInput?.(value, event.target as HTMLInputElement)
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
- function handleValueChange(value: null | string, target: HTMLInputElement): void {
56
- const parsed = toNumber(value)
57
- const bounded = parsed === null ? null : clamp(parsed)
58
- rawValue.value = null
59
- model.value = bounded
60
- props.onValueChange?.(bounded, target)
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
- function handleValueInput(value: null | string, target: HTMLInputElement | null): void {
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 toNumber(value: null | string): null | number {
71
- if (value === null || value.trim() === '') {
72
- return null
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
- const number = Number(value.replace(',', '.'))
76
- return Number.isFinite(number) ? number : null
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
- type="decimal"
91
- :min="props.min"
92
- :max="props.max"
93
- :step="props.step"
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.left"
103
- #left
219
+ v-if="$slots.leading"
220
+ #leading
104
221
  >
105
- <slot name="left" />
222
+ <slot name="leading" />
106
223
  </template>
107
224
  <template
108
- v-if="$slots.right"
109
- #right
225
+ v-if="$slots.trailing"
226
+ #trailing
110
227
  >
111
- <slot name="right" />
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,7 @@
1
+ import type { defineNuxtLink } from 'nuxt/app'
2
+
3
+ declare module 'vue' {
4
+ interface GlobalComponents {
5
+ NuxtLink: ReturnType<typeof defineNuxtLink>
6
+ }
7
+ }
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@brickflow/ui",
3
- "version": "0.0.54",
3
+ "version": "0.0.56",
4
4
  "type": "module",
5
5
  "description": "brickflow UI Nuxt module.",
6
6
  "files": [
@@ -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>