@brickflow/ui 0.0.56 → 0.0.58

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.
Files changed (47) hide show
  1. package/dist/module.d.mts +4 -2
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +35 -7
  4. package/dist/runtime/brickflow-ui.d.ts +1 -0
  5. package/dist/runtime/components/Button/index.vue +5 -3
  6. package/dist/runtime/components/Dialog/DemoContent.vue +33 -0
  7. package/dist/runtime/components/Dialog/DemoForm.vue +54 -0
  8. package/dist/runtime/components/Dialog/DemoSimple.vue +23 -0
  9. package/dist/runtime/components/Dialog/DemoStack.vue +41 -0
  10. package/dist/runtime/components/Dialog/Host.vue +654 -0
  11. package/dist/runtime/components/Dialog/basic.demo.vue +27 -0
  12. package/dist/runtime/components/Dialog/form.demo.vue +34 -0
  13. package/dist/runtime/components/Dialog/index.vue +53 -0
  14. package/dist/runtime/components/Dialog/scroll.demo.vue +27 -0
  15. package/dist/runtime/components/Dialog/stack.demo.vue +19 -0
  16. package/dist/runtime/components/Form/index.vue +46 -1
  17. package/dist/runtime/components/Form/login.demo.vue +12 -1
  18. package/dist/runtime/components/Form/rules.demo.vue +108 -0
  19. package/dist/runtime/components/Icon/gallery.demo.vue +1 -1
  20. package/dist/runtime/components/Icon/index.vue +3 -1
  21. package/dist/runtime/components/Input/index.vue +151 -27
  22. package/dist/runtime/components/Input/label.demo.vue +15 -3
  23. package/dist/runtime/components/Input/mask.demo.vue +73 -0
  24. package/dist/runtime/components/Input/variants.demo.vue +16 -0
  25. package/dist/runtime/components/InputNumber/controls.demo.vue +145 -0
  26. package/dist/runtime/components/InputNumber/denominator.demo.vue +1 -1
  27. package/dist/runtime/components/InputNumber/index.vue +29 -8
  28. package/dist/runtime/components/TransitionSize/duration.demo.vue +88 -0
  29. package/dist/runtime/components/TransitionSize/flex.demo.vue +112 -0
  30. package/dist/runtime/components/TransitionSize/height.demo.vue +88 -0
  31. package/dist/runtime/components/TransitionSize/index.vue +69 -27
  32. package/dist/runtime/components/TransitionSize/width.demo.vue +88 -0
  33. package/dist/runtime/composables/useBrickflow.d.ts +2 -0
  34. package/dist/runtime/composables/useDialog.d.ts +38 -0
  35. package/dist/runtime/composables/useDialog.js +40 -0
  36. package/dist/runtime/composables/useFieldRules.d.ts +16 -0
  37. package/dist/runtime/composables/useFieldRules.js +42 -0
  38. package/dist/runtime/composables/useScreen.d.ts +8 -0
  39. package/dist/runtime/plugins/dialog.d.ts +3 -0
  40. package/dist/runtime/plugins/dialog.js +22 -0
  41. package/dist/runtime/tailwind.d.ts +1 -1
  42. package/dist/runtime/tailwind.js +2 -2
  43. package/dist/runtime/utils/rules.d.ts +34 -0
  44. package/dist/runtime/utils/rules.js +68 -0
  45. package/dist/types.d.mts +4 -0
  46. package/package.json +7 -6
  47. package/dist/runtime/icon-font.d.ts +0 -4
@@ -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 marginName = computed(() => {
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 !overflow-hidden will-change-transform').split(
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
- shrink(el)
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
- if (el[scrollName.value] === 0) {
67
- el.style[sizeName.value] = ''
68
- } else {
69
- el.style[sizeName.value] = `${el[scrollName.value]}px`
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[marginName.value] = ''
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.add(...expandClass.value)
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.transitionDuration = `${props.duration}ms`
146
+ el.style.flexGrow = '0'
114
147
  el.style[sizeName.value] = '0'
115
- el.style.padding = '0'
148
+ shrinkPadding(el)
116
149
  if (props.flexParent) {
117
150
  gap.value = parseInt(window.getComputedStyle(props.flexParent)?.gap) || 0
118
- el.style[marginName.value] = `-${gap.value}px`
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,38 @@
1
+ import { type Component, type InjectionKey } from 'vue';
2
+ import type { ScreenKey } from './useScreen.js';
3
+ export interface DialogController {
4
+ close(id?: number): void;
5
+ closeAll(): void;
6
+ open<C extends Component>(component: C, props: DialogProps<C>, options?: DialogOptions): DialogHandle;
7
+ readonly stack: DialogEntry[];
8
+ }
9
+ export interface DialogEntry {
10
+ component: Component;
11
+ id: number;
12
+ options: Required<DialogOptions> & {
13
+ elasticHeight: boolean;
14
+ };
15
+ props: Record<string, unknown>;
16
+ }
17
+ export interface DialogHandle {
18
+ close: () => void;
19
+ id: number;
20
+ }
21
+ export interface DialogOptions {
22
+ /** Dialog position, optionally overridden for each screen range. Defaults to center. */
23
+ placement?: DialogPlacementConfig;
24
+ /** Prevent backdrop, Escape, and swipe dismissal. Defaults to false. */
25
+ preventClose?: boolean;
26
+ }
27
+ export type DialogPlacement = 'bottom' | 'center' | 'fullscreen';
28
+ export type DialogPlacementConfig = DialogPlacement | Record<ScreenKey, DialogPlacement>;
29
+ type DialogProps<C> = C extends new (...args: never[]) => {
30
+ $props: infer P;
31
+ } ? Omit<P, 'class' | 'key' | 'ref' | 'style'> : Record<string, unknown>;
32
+ export declare const dialogKey: InjectionKey<DialogController>;
33
+ export declare function createDialogController(defaultOptions?: DialogOptions & {
34
+ elasticHeight?: boolean;
35
+ }): DialogController;
36
+ export declare function useDialog(): DialogController;
37
+ export {};
38
+ //# sourceMappingURL=useDialog.d.ts.map
@@ -0,0 +1,40 @@
1
+ import { inject, markRaw, reactive } from "vue";
2
+ export const dialogKey = Symbol("brickflow-dialog");
3
+ export function createDialogController(defaultOptions = {}) {
4
+ const stack = reactive([]);
5
+ let nextId = 0;
6
+ return {
7
+ close(id) {
8
+ const index = id === void 0 ? stack.length - 1 : stack.findIndex((entry) => entry.id === id);
9
+ if (index !== -1) {
10
+ stack.splice(index, 1);
11
+ }
12
+ },
13
+ closeAll() {
14
+ stack.splice(0);
15
+ },
16
+ open(component, props, options = {}) {
17
+ nextId += 1;
18
+ const id = nextId;
19
+ stack.push({
20
+ component: markRaw(component),
21
+ id,
22
+ options: {
23
+ elasticHeight: defaultOptions.elasticHeight ?? true,
24
+ placement: options.placement ?? defaultOptions.placement ?? "center",
25
+ preventClose: options.preventClose ?? defaultOptions.preventClose ?? false
26
+ },
27
+ props: { ...props }
28
+ });
29
+ return { close: () => this.close(id), id };
30
+ },
31
+ stack
32
+ };
33
+ }
34
+ export function useDialog() {
35
+ const dialog = inject(dialogKey, void 0);
36
+ if (!dialog) {
37
+ throw new Error("useDialog() requires the @brickflow/ui Nuxt module");
38
+ }
39
+ return dialog;
40
+ }
@@ -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
+ }
@@ -1,3 +1,11 @@
1
+ export type ScreenKey = keyof ScreenState;
2
+ interface ScreenState {
3
+ dk: boolean;
4
+ lp: boolean;
5
+ mb: boolean;
6
+ ms: boolean;
7
+ tb: boolean;
8
+ }
1
9
  declare const screen: {
2
10
  readonly dk: boolean;
3
11
  readonly dkClass: "dkHidden";
@@ -0,0 +1,3 @@
1
+ declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
3
+ //# sourceMappingURL=dialog.d.ts.map
@@ -0,0 +1,22 @@
1
+ import { defineNuxtPlugin } from "nuxt/app";
2
+ import { createVNode, render } from "vue";
3
+ import { uiConfig } from "#brickflow-ui";
4
+ import DialogHost from "../components/Dialog/Host.vue";
5
+ import { createDialogController, dialogKey } from "../composables/useDialog.js";
6
+ export default defineNuxtPlugin((nuxtApp) => {
7
+ const dialog = createDialogController({
8
+ elasticHeight: uiConfig.dialog.elasticHeight,
9
+ placement: uiConfig.dialog.placement
10
+ });
11
+ nuxtApp.vueApp.provide(dialogKey, dialog);
12
+ if (import.meta.client) {
13
+ nuxtApp.hook("app:mounted", () => {
14
+ const mountPoint = document.createElement("div");
15
+ mountPoint.id = "brickflow-dialog-host";
16
+ document.body.appendChild(mountPoint);
17
+ const vnode = createVNode(DialogHost, { dialog });
18
+ vnode.appContext = nuxtApp.vueApp._context;
19
+ render(vnode, mountPoint);
20
+ });
21
+ }
22
+ });
@@ -6,7 +6,7 @@ export interface BrickflowUiConfig<TStyles = BrickflowUiStyles, TConfig = Brickf
6
6
  export type BrickflowUiConfigObject = {
7
7
  readonly [key: string]: BrickflowUiConfigValue;
8
8
  };
9
- export type BrickflowUiConfigValue = BrickflowUiConfigObject | BrickflowUiStyleReference | string;
9
+ export type BrickflowUiConfigValue = boolean | BrickflowUiConfigObject | BrickflowUiStyleReference | number | string;
10
10
  export interface BrickflowUiStyleReference {
11
11
  readonly [UI_STYLE_REFERENCE]: readonly string[];
12
12
  }
@@ -44,14 +44,14 @@ function validateUiConfig(value, path = "uiConfig") {
44
44
  }
45
45
  for (const [key, childValue] of Object.entries(value)) {
46
46
  const childPath = `${path}.${key}`;
47
- if (typeof childValue === "string" || isBrickflowUiStyleReference(childValue)) {
47
+ if (typeof childValue === "string" || typeof childValue === "boolean" || typeof childValue === "number" && Number.isFinite(childValue) || isBrickflowUiStyleReference(childValue)) {
48
48
  continue;
49
49
  }
50
50
  if (isRecord(childValue)) {
51
51
  validateUiConfig(childValue, childPath);
52
52
  continue;
53
53
  }
54
- throw new TypeError(`${childPath} must be a string, UI_STYLE reference, or an object.`);
54
+ throw new TypeError(`${childPath} must be a string, boolean, finite number, UI_STYLE reference, or an object.`);
55
55
  }
56
56
  }
57
57
  function validateUiStyles(value, path = "uiStyles") {
@@ -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
@@ -0,0 +1,68 @@
1
+ import { useBrickflow } from "../composables/useBrickflow.js";
2
+ export const defaultRuleMessages = {
3
+ email: () => "Enter a valid email",
4
+ length: ({ length: expectedLength }) => `Must be exactly ${expectedLength} characters`,
5
+ maxLength: ({ max }) => `Maximum ${max} characters`,
6
+ minLength: ({ min }) => `Minimum ${min} characters`,
7
+ required: () => "Required field",
8
+ url: () => "Enter a valid URL"
9
+ };
10
+ export const rule = { email, length, maxLength, minLength, regex, required, url };
11
+ function email() {
12
+ const messages = { ...defaultRuleMessages, ...useBrickflow().rules };
13
+ return (value) => {
14
+ if (!value) {
15
+ return true;
16
+ }
17
+ const parts = value.split("@");
18
+ const domain = parts[1];
19
+ return parts.length === 2 && Boolean(parts[0]) && Boolean(domain?.includes(".")) && !/\s/.test(value) || messages.email();
20
+ };
21
+ }
22
+ function length(expectedLength) {
23
+ const messages = { ...defaultRuleMessages, ...useBrickflow().rules };
24
+ return (value) => !value || value.length === expectedLength || messages.length({ length: expectedLength });
25
+ }
26
+ function maxLength(max) {
27
+ const messages = { ...defaultRuleMessages, ...useBrickflow().rules };
28
+ return (value) => !value || value.length <= max || messages.maxLength({ max });
29
+ }
30
+ function minLength(min) {
31
+ const messages = { ...defaultRuleMessages, ...useBrickflow().rules };
32
+ return (value) => !value || value.length >= min || messages.minLength({ min });
33
+ }
34
+ function regex(pattern, message) {
35
+ return (value) => {
36
+ pattern.lastIndex = 0;
37
+ return !value || pattern.test(value) || (typeof message === "function" ? message() : message);
38
+ };
39
+ }
40
+ function required() {
41
+ const messages = { ...defaultRuleMessages, ...useBrickflow().rules };
42
+ return (value) => {
43
+ if (typeof value === "string") {
44
+ return value.trim().length > 0 || messages.required();
45
+ }
46
+ if (typeof value === "boolean") {
47
+ return value || messages.required();
48
+ }
49
+ if (Array.isArray(value)) {
50
+ return value.length > 0 || messages.required();
51
+ }
52
+ return value !== null && value !== void 0 || messages.required();
53
+ };
54
+ }
55
+ function url() {
56
+ const messages = { ...defaultRuleMessages, ...useBrickflow().rules };
57
+ return (value) => {
58
+ if (!value) {
59
+ return true;
60
+ }
61
+ try {
62
+ const parsed = new URL(value);
63
+ return ["http:", "https:"].includes(parsed.protocol) && Boolean(parsed.hostname) || messages.url();
64
+ } catch {
65
+ return messages.url();
66
+ }
67
+ };
68
+ }
package/dist/types.d.mts CHANGED
@@ -1,7 +1,11 @@
1
1
  export { type BrickflowConfig } from '../dist/runtime/composables/useBrickflow.js'
2
2
 
3
+ export { type DialogController, type DialogHandle, type DialogOptions } from '../dist/runtime/composables/useDialog.js'
4
+
3
5
  export { type BrickflowI18n, type BrickflowRouteLocationParam } from '../dist/runtime/composables/useTranslate.js'
4
6
 
7
+ export { type Rule, type RuleMessages, type rule } from '../dist/runtime/utils/rules.js'
8
+
5
9
  export { default } from './module.mjs'
6
10
 
7
11
  export { type ModuleOptions } from './module.mjs'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brickflow/ui",
3
- "version": "0.0.56",
3
+ "version": "0.0.58",
4
4
  "type": "module",
5
5
  "description": "brickflow UI Nuxt module.",
6
6
  "files": [
@@ -35,17 +35,18 @@
35
35
  "@tailwindcss/vite": "4.3.3",
36
36
  "fantasticon": "4.1.0",
37
37
  "jiti": "2.7.0",
38
- "tailwind-merge": "3.6.0",
38
+ "maska": "3.2.2",
39
+ "tailwind-merge": "3.7.0",
39
40
  "tailwindcss": "4.3.3"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@brickflow/lint": "workspace:*",
43
44
  "@brickflow/prettier": "workspace:*",
44
- "@nuxt/kit": "4.5.2",
45
+ "@nuxt/kit": "4.6.3",
45
46
  "@nuxt/module-builder": "1.0.3",
46
- "nuxt": "4.5.2",
47
- "vue": "3.5.42",
48
- "vue-tsc": "3.3.11"
47
+ "nuxt": "4.6.3",
48
+ "vue": "3.5.44",
49
+ "vue-tsc": "3.3.14"
49
50
  },
50
51
  "author": "",
51
52
  "license": "ISC"
@@ -1,4 +0,0 @@
1
- declare module '#brickflow-ui-icons' {
2
- export const firstIconName: string
3
- export const iconNames: readonly string[]
4
- }