@brickflow/ui 0.0.54 → 0.0.55

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.55",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.3",
10
10
  "unbuild": "3.6.1"
@@ -46,12 +46,12 @@
46
46
  disabled?: boolean
47
47
  icon?: string
48
48
  label?: string
49
- onBlur?: (value: null | string, target: HTMLInputElement) => void
50
- onFocus?: (value: null | string, target: HTMLInputElement | null) => void
49
+ onBlur?: (value: string, target: HTMLInputElement) => void
50
+ onChange?: (value: string, target: HTMLInputElement) => void
51
+ onFocus?: (value: string, target: HTMLInputElement | null) => void
51
52
  onIconClick?: () => void
53
+ onInput?: (value: string, target: HTMLInputElement | null) => void
52
54
  onTrailingIconClick?: () => void
53
- onValueChange?: (value: null | string, target: HTMLInputElement) => void
54
- onValueInput?: (value: null | string, target: HTMLInputElement | null) => void
55
55
  placeholder?: string
56
56
  readonly?: boolean
57
57
  size?: InputSize
@@ -68,11 +68,11 @@
68
68
  icon: undefined,
69
69
  label: undefined,
70
70
  onBlur: undefined,
71
+ onChange: undefined,
71
72
  onFocus: undefined,
72
73
  onIconClick: undefined,
74
+ onInput: undefined,
73
75
  onTrailingIconClick: undefined,
74
- onValueChange: undefined,
75
- onValueInput: undefined,
76
76
  placeholder: '',
77
77
  readonly: false,
78
78
  size: 'md',
@@ -83,8 +83,7 @@
83
83
  },
84
84
  )
85
85
 
86
- const model = shallowRef<null | string>('')
87
- // const model = defineModel<null | number | string>({ default: '' })
86
+ const model = shallowRef('')
88
87
 
89
88
  const attrs = useAttrs()
90
89
  const slots = useSlots()
@@ -104,7 +103,7 @@
104
103
 
105
104
  const fieldId = computed(() => String(attrs.id ?? generatedId))
106
105
  const descriptionId = computed(() => `${fieldId.value}-description`)
107
- const currentValue = computed(() => (props.value === undefined ? model.value : props.value))
106
+ const currentValue = computed(() => (props.value === undefined ? model.value : (props.value ?? '')))
108
107
  const widthMeasureText = computed(() => currentValue.value || props.placeholder || ' ')
109
108
  const isEmptyField = computed(() => {
110
109
  return Boolean(currentValue.value)
@@ -177,23 +176,23 @@
177
176
  props.onBlur?.(currentValue.value, event.target as HTMLInputElement)
178
177
  }
179
178
 
179
+ function handleChange(event: Event): void {
180
+ const target = event.target as HTMLInputElement
181
+ props.onChange?.(target.value, target)
182
+ }
183
+
180
184
  function handleFocus(event: FocusEvent): void {
181
185
  hasFocus.value = true
182
186
  props.onFocus?.(currentValue.value, event.target as HTMLInputElement)
183
187
  }
184
188
 
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 {
189
+ function handleInput(event: Event): void {
191
190
  setValue((event.target as HTMLInputElement).value)
192
191
  }
193
192
 
194
- function setValue(value: null | string): void {
193
+ function setValue(value: string): void {
195
194
  model.value = value
196
- props.onValueInput?.(value, inputRef.value)
195
+ props.onInput?.(value, inputRef.value)
197
196
  }
198
197
 
199
198
  defineExpose({ clear, fieldId, focus, hasFocus, inputRef })
@@ -252,8 +251,8 @@
252
251
  :aria-describedby="hasDescription ? descriptionId : undefined"
253
252
  :class="fieldClasses"
254
253
  :style="fieldStyle"
255
- @input="onInput"
256
- @change="onChange"
254
+ @input="handleInput"
255
+ @change="handleChange"
257
256
  @focus="handleFocus"
258
257
  @blur="handleBlur"
259
258
  />
@@ -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"
@@ -9,29 +9,25 @@
9
9
 
10
10
  import InputNumber from './index.vue'
11
11
 
12
- const quantity = ref<null | number>(null)
13
- const price = ref<null | number>(25)
12
+ const price = ref<number>(25)
13
+ function handleInput(event: number): void {
14
+ price.value = event
15
+ }
14
16
  </script>
15
17
 
16
18
  <template>
17
19
  <div class="flex max-w-sm flex-col gap-4">
18
20
  <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"
21
+ :value="price"
28
22
  label="Price"
29
- description="Changes in steps of five."
23
+ description="Min: 0 — Max: 200"
30
24
  :min="0"
31
- :max="100"
25
+ :max="200"
32
26
  :step="5"
33
- block
34
- />
35
- <p class="text-sm text-gray-400">Quantity: {{ quantity ?? '—' }} · Price: {{ price ?? '—' }}</p>
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>
36
32
  </div>
37
33
  </template>
@@ -1,79 +1,95 @@
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
+ denominator?: number
12
+ description?: string
13
+ disabled?: boolean
14
+ label?: string
10
15
  max?: number
11
16
  min?: number
12
- onValueChange?: (value: null | number, target: HTMLInputElement) => void
13
- onValueInput?: (value: null | number, target: HTMLInputElement | null) => void
17
+ nullable?: T
18
+ onBlur?: (value: true extends NoInfer<T> ? null | number : number) => void
19
+ onFocus?: (value: true extends NoInfer<T> ? null | number : number) => void
20
+ onInput?: (value: true extends NoInfer<T> ? null | number : number) => void
21
+ onKeydown?: (event: KeyboardEvent) => void
14
22
  step?: number
15
- value?: null | number
23
+ value: true extends NoInfer<T> ? null | number : number
16
24
  }>(),
17
25
  {
26
+ denominator: 0,
27
+ description: undefined,
28
+ disabled: false,
29
+ label: undefined,
18
30
  max: undefined,
19
31
  min: undefined,
32
+ nullable: undefined,
33
+ onBlur: undefined,
34
+ onFocus: undefined,
35
+ onInput: undefined,
36
+ onKeydown: undefined,
20
37
  onValueChange: undefined,
21
- onValueInput: undefined,
22
38
  step: 1,
23
- value: undefined,
24
39
  },
25
40
  )
26
41
 
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)),
32
- )
33
-
34
- function clamp(value: number): number {
35
- return Math.min(props.max ?? Infinity, Math.max(props.min ?? -Infinity, value))
36
- }
42
+ const inputValue = shallowRef(prepareValue(props.value))
37
43
 
38
- function handleBlur(): void {
39
- rawValue.value = null
40
- }
44
+ watch(
45
+ () => props.value,
46
+ () => {
47
+ inputValue.value = prepareValue(props.value)
48
+ },
49
+ )
41
50
 
42
- function handleKeydown(event: KeyboardEvent): void {
43
- if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') {
51
+ function handleBlur(value: string, target: HTMLInputElement): void {
52
+ if (!target) {
44
53
  return
45
54
  }
46
-
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
- }
54
-
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
- }
62
-
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)
55
+ const nextValue = prepareValue(props.value)
56
+ target.value = nextValue
57
+ inputValue.value = nextValue
58
+ props.onBlur?.(props.value)
68
59
  }
60
+ const allowedRegexp = /^-?\d*(?:\.\d*)?$/
61
+ const validNumberRegexp = /^-?(?:\d+(?:\.\d+)?|\.\d+)$/
69
62
 
70
- function toNumber(value: null | string): null | number {
71
- if (value === null || value.trim() === '') {
72
- return null
63
+ function handleInput(rawValue: string, target: HTMLInputElement | null): void {
64
+ if (!target) {
65
+ return
73
66
  }
67
+ const nextValue = rawValue.replace(/,/, '.')
68
+ target.value = nextValue
74
69
 
75
- const number = Number(value.replace(',', '.'))
76
- return Number.isFinite(number) ? number : null
70
+ if (isEmpty(nextValue)) {
71
+ return
72
+ }
73
+ if (!allowedRegexp.test(nextValue)) {
74
+ target.value = inputValue.value
75
+ return
76
+ }
77
+ if (!validNumberRegexp.test(nextValue)) {
78
+ return
79
+ }
80
+ let nextValueNumber = Number(nextValue)
81
+ if (!isEmpty(props.min) && nextValueNumber < props.min) {
82
+ target.value = prepareValue(props.min)
83
+ nextValueNumber = props.min
84
+ }
85
+ if (!isEmpty(props.max) && nextValueNumber > props.max) {
86
+ target.value = prepareValue(props.max)
87
+ nextValueNumber = props.max
88
+ }
89
+ props.onInput?.(nextValueNumber)
90
+ }
91
+ function prepareValue(value: typeof props.value): string {
92
+ return value === null ? '' : String(value)
77
93
  }
78
94
  </script>
79
95
 
@@ -81,16 +97,15 @@
81
97
  <Input
82
98
  v-bind="{
83
99
  ...$attrs,
84
- onValueInput: handleValueInput,
85
- onValueChange: handleValueChange,
86
- onBlur: handleBlur,
87
- onKeydown: handleKeydown,
100
+ // onBlur: handleBlur,
101
+ // onKeydown: handleKeydown,
88
102
  }"
89
103
  :value="inputValue"
104
+ :label="props.label"
105
+ :description="props.description"
90
106
  type="decimal"
91
- :min="props.min"
92
- :max="props.max"
93
- :step="props.step"
107
+ @input="handleInput"
108
+ @blur="handleBlur"
94
109
  >
95
110
  <template
96
111
  v-if="$slots.label"
@@ -99,16 +114,16 @@
99
114
  <slot name="label" />
100
115
  </template>
101
116
  <template
102
- v-if="$slots.left"
103
- #left
117
+ v-if="$slots.leading"
118
+ #leading
104
119
  >
105
- <slot name="left" />
120
+ <slot name="leading" />
106
121
  </template>
107
122
  <template
108
- v-if="$slots.right"
109
- #right
123
+ v-if="$slots.trailing"
124
+ #trailing
110
125
  >
111
- <slot name="right" />
126
+ <slot name="trailing" />
112
127
  </template>
113
128
  <template
114
129
  v-if="$slots.error"
@@ -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.55",
4
4
  "type": "module",
5
5
  "description": "brickflow UI Nuxt module.",
6
6
  "files": [