@sfxcode/formkit-nuxt-ui 0.7.2 → 0.7.4

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
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "0.7.2",
7
+ "version": "0.7.4",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
@@ -1,8 +1,8 @@
1
1
  import type { FormKitSchemaDefinition } from '@formkit/core';
2
2
  import type { PropType } from 'vue';
3
- declare var __VLS_6: {};
3
+ declare var __VLS_12: {};
4
4
  type __VLS_Slots = {} & {
5
- default?: (props: typeof __VLS_6) => any;
5
+ default?: (props: typeof __VLS_12) => any;
6
6
  };
7
7
  declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
8
8
  data: {
@@ -30,11 +30,19 @@ if (props.data) {
30
30
  </script>
31
31
 
32
32
  <template>
33
- <FormKitSchema
34
- v-if="schema"
35
- :schema="schema"
36
- :data="formData"
37
- />
33
+ <FormKit
34
+ v-model="formData"
35
+ :actions="false"
36
+ :form-class="formClass"
37
+ type="form"
38
+ >
39
+ <FormKitSchema
40
+ v-if="schema"
41
+ :schema="schema"
42
+ :data="formData"
43
+ />
44
+ </FormKit>
45
+
38
46
  <slot />
39
47
  <FuDataDebug
40
48
  v-if="debugData"
@@ -1,8 +1,8 @@
1
1
  import type { FormKitSchemaDefinition } from '@formkit/core';
2
2
  import type { PropType } from 'vue';
3
- declare var __VLS_6: {};
3
+ declare var __VLS_12: {};
4
4
  type __VLS_Slots = {} & {
5
- default?: (props: typeof __VLS_6) => any;
5
+ default?: (props: typeof __VLS_12) => any;
6
6
  };
7
7
  declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
8
8
  data: {
@@ -15,7 +15,6 @@ defineProps({
15
15
  <template>
16
16
  <UIcon
17
17
  :name="name"
18
- :class="iconClass"
19
18
  @click="onClick"
20
19
  />
21
20
  </template>
@@ -36,20 +36,20 @@ const { containerClass, iconClass, leadingIconName, trailingIconName } = useForm
36
36
  <FUIcon
37
37
  v-if="leadingIconName"
38
38
  :name="leadingIconName"
39
- :icon-class="iconClass"
39
+ :class="iconClass"
40
40
  :on-click="context?.onLeadingIconClicked"
41
41
  />
42
42
  <FUIcon
43
43
  v-if="booleanIcon"
44
44
  :name="booleanIcon"
45
- :icon-class="iconClass"
45
+ :class="iconClass"
46
46
  :on-click="context?.onIconClicked"
47
47
  />
48
48
  <span>{{ displayValue }}</span>
49
49
  <FUIcon
50
50
  v-if="trailingIconName"
51
51
  :name="trailingIconName"
52
- :icon-class="iconClass"
52
+ :class="iconClass"
53
53
  :on-click="context?.onTrailingIconClicked"
54
54
  />
55
55
  </div>
@@ -30,7 +30,7 @@ const { containerClass, iconClass, leadingIconName, trailingIconName } = useForm
30
30
  <FUIcon
31
31
  v-if="leadingIconName"
32
32
  :name="leadingIconName"
33
- :icon-class="iconClass"
33
+ :class="iconClass"
34
34
  :on-click="context?.onLeadingIconClicked"
35
35
  />
36
36
  <NuxtTime
@@ -45,7 +45,7 @@ const { containerClass, iconClass, leadingIconName, trailingIconName } = useForm
45
45
  <FUIcon
46
46
  v-if="trailingIconName"
47
47
  :name="trailingIconName"
48
- :icon-class="iconClass"
48
+ :class="iconClass"
49
49
  :on-click="context?.onTrailingIconClicked"
50
50
  />
51
51
  </div>
@@ -46,7 +46,7 @@ const linkClass = computed(() => {
46
46
  <FUIcon
47
47
  v-if="leadingIconName"
48
48
  :name="leadingIconName"
49
- :icon-class="iconClass"
49
+ :class="iconClass"
50
50
  :on-click="context?.onLeadingIconClicked"
51
51
  />
52
52
  <ULink
@@ -71,7 +71,7 @@ const linkClass = computed(() => {
71
71
  <FUIcon
72
72
  v-if="trailingIconName"
73
73
  :name="trailingIconName"
74
- :icon-class="iconClass"
74
+ :class="iconClass"
75
75
  :on-click="context?.onTrailingIconClicked"
76
76
  />
77
77
  </div>
@@ -56,7 +56,7 @@ const { containerClass, iconClass, leadingIconName, trailingIconName } = useForm
56
56
  <FUIcon
57
57
  v-if="leadingIconName"
58
58
  :name="leadingIconName"
59
- :icon-class="iconClass"
59
+ :class="iconClass"
60
60
  :on-click="context?.onLeadingIconClicked"
61
61
  />
62
62
 
@@ -136,7 +136,7 @@ const { containerClass, iconClass, leadingIconName, trailingIconName } = useForm
136
136
  <FUIcon
137
137
  v-if="trailingIconName"
138
138
  :name="trailingIconName"
139
- :icon-class="iconClass"
139
+ :class="iconClass"
140
140
  :on-click="context?.onTrailingIconClicked"
141
141
  />
142
142
  </div>
@@ -38,14 +38,14 @@ const { containerClass, iconClass, leadingIconName, trailingIconName } = useForm
38
38
  <FUIcon
39
39
  v-if="leadingIconName"
40
40
  :name="leadingIconName"
41
- :icon-class="iconClass"
41
+ :class="iconClass"
42
42
  :on-click="context?.onLeadingIconClicked"
43
43
  />
44
44
  <span>{{ displayValue }}</span>
45
45
  <FUIcon
46
46
  v-if="trailingIconName"
47
47
  :name="trailingIconName"
48
- :icon-class="iconClass"
48
+ :class="iconClass"
49
49
  :on-click="context?.onTrailingIconClicked"
50
50
  />
51
51
  </div>
@@ -10,6 +10,7 @@ export interface FormKitOutputTextProps {
10
10
  trailing?: boolean;
11
11
  trailingIcon?: string;
12
12
  variant?: 'outline' | 'soft' | 'subtle' | 'ghost' | 'none';
13
+ outputType?: 'text' | 'email' | 'url' | 'tel' | 'color' | 'duration';
13
14
  }
14
15
  declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
15
16
  context: {
@@ -1,6 +1,8 @@
1
1
  <script setup>
2
2
  import { computed } from "vue";
3
3
  import { useFormKitOutput } from "../../utils/useFormKitOutput";
4
+ import { convertColorToHex } from "../../utils/colorConverter";
5
+ import { formattedDuration } from "../../utils/durationConverter";
4
6
  import FUIcon from "./FUIcon.vue";
5
7
  const props = defineProps({
6
8
  context: {
@@ -8,7 +10,42 @@ const props = defineProps({
8
10
  required: true
9
11
  }
10
12
  });
11
- const displayValue = computed(() => props.context._value ?? "");
13
+ const outputType = computed(() => props.context.outputType ?? "text");
14
+ const displayValue = computed(() => {
15
+ let result = props.context._value ?? "";
16
+ if (outputType.value === "duration") {
17
+ result = formattedDuration(result);
18
+ }
19
+ return result;
20
+ });
21
+ const isLink = computed(() => ["email", "url", "tel"].includes(outputType.value));
22
+ const linkHref = computed(() => {
23
+ const value = displayValue.value;
24
+ if (!value)
25
+ return "";
26
+ switch (outputType.value) {
27
+ case "email":
28
+ return value.startsWith("mailto:") ? value : `mailto:${value}`;
29
+ case "tel":
30
+ return value.startsWith("tel:") ? value : `tel:${value}`;
31
+ case "url":
32
+ return value;
33
+ default:
34
+ return "";
35
+ }
36
+ });
37
+ const colorValue = computed(() => {
38
+ if (outputType.value === "color") {
39
+ return convertColorToHex(displayValue.value);
40
+ }
41
+ return void 0;
42
+ });
43
+ const colorStyle = computed(() => {
44
+ if (outputType.value === "color") {
45
+ return `color: ${colorValue.value} `;
46
+ }
47
+ return " ";
48
+ });
12
49
  const { containerClass, iconClass, leadingIconName, trailingIconName } = useFormKitOutput(props.context);
13
50
  </script>
14
51
 
@@ -21,14 +58,32 @@ const { containerClass, iconClass, leadingIconName, trailingIconName } = useForm
21
58
  <FUIcon
22
59
  v-if="leadingIconName"
23
60
  :name="leadingIconName"
24
- :icon-class="iconClass"
61
+ :class="iconClass"
62
+ :style="colorStyle"
25
63
  :on-click="context?.onLeadingIconClicked"
26
64
  />
27
- <span>{{ displayValue }}</span>
65
+
66
+ <!-- Link output types (email, url, tel) -->
67
+ <ULink
68
+ v-if="isLink && displayValue"
69
+ :href="linkHref"
70
+ :target="outputType === 'url' ? '_blank' : void 0"
71
+ :rel="outputType === 'url' ? 'noopener noreferrer' : void 0"
72
+ >
73
+ {{ displayValue }}
74
+ </ULink>
75
+
76
+ <!-- Default text output -->
77
+ <span
78
+ v-else
79
+ :style="colorStyle"
80
+ >{{ displayValue }}</span>
81
+
28
82
  <FUIcon
29
83
  v-if="trailingIconName"
30
84
  :name="trailingIconName"
31
- :icon-class="iconClass"
85
+ :class="iconClass"
86
+ :style="colorStyle"
32
87
  :on-click="context?.onTrailingIconClicked"
33
88
  />
34
89
  </div>
@@ -10,6 +10,7 @@ export interface FormKitOutputTextProps {
10
10
  trailing?: boolean;
11
11
  trailingIcon?: string;
12
12
  variant?: 'outline' | 'soft' | 'subtle' | 'ghost' | 'none';
13
+ outputType?: 'text' | 'email' | 'url' | 'tel' | 'color' | 'duration';
13
14
  }
14
15
  declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
15
16
  context: {
@@ -6,7 +6,7 @@ import FUOutputLink from "../components/output/FUOutputLink.vue";
6
6
  import FUOutputNumber from "../components/output/FUOutputNumber.vue";
7
7
  import FUOutputList from "../components/output/FUOutputList.vue";
8
8
  export const nuxtUIOutputTextDefinition = createInput(FUOutputText, {
9
- props: ["size", "color", "variant", "icon", "leadingIcon", "trailingIcon", "leading", "trailing", "onIconClicked", "onLeadingIconClicked", "onTrailingIconClicked"],
9
+ props: ["size", "color", "variant", "icon", "leadingIcon", "trailingIcon", "leading", "trailing", "onIconClicked", "onLeadingIconClicked", "onTrailingIconClicked", "outputType"],
10
10
  family: "NuxtUIOutput"
11
11
  });
12
12
  export const nuxtUIOutputBooleanDefinition = createInput(FUOutputBoolean, {
@@ -0,0 +1,71 @@
1
+ import type { FormKitInputs } from '@formkit/inputs';
2
+ declare module '@formkit/inputs' {
3
+ interface FormKitInputProps<Props extends FormKitInputs<Props>> {
4
+ nuxtUICheckbox: {
5
+ type: 'nuxtUICheckbox';
6
+ };
7
+ nuxtUICheckboxGroup: {
8
+ type: 'nuxtUICheckboxGroup';
9
+ };
10
+ nuxtUIColorPicker: {
11
+ type: 'nuxtUIColorPicker';
12
+ };
13
+ nuxtUIInput: {
14
+ type: 'nuxtUIInput';
15
+ };
16
+ nuxtUIInputDate: {
17
+ type: 'nuxtUIInputDate';
18
+ };
19
+ nuxtUIInputMenu: {
20
+ type: 'nuxtUIInputMenu';
21
+ };
22
+ nuxtUIInputNumber: {
23
+ type: 'nuxtUIInputNumber';
24
+ };
25
+ nuxtUIInputTags: {
26
+ type: 'nuxtUIInputTags';
27
+ };
28
+ nuxtUIInputTime: {
29
+ type: 'nuxtUIInputTime';
30
+ };
31
+ nuxtUIPinInput: {
32
+ type: 'nuxtUIPinInput';
33
+ };
34
+ nuxtUIRadioGroup: {
35
+ type: 'nuxtUIRadioGroup';
36
+ };
37
+ nuxtUISelect: {
38
+ type: 'nuxtUISelect';
39
+ };
40
+ nuxtUISelectMenu: {
41
+ type: 'nuxtUISelectMenu';
42
+ };
43
+ nuxtUISlider: {
44
+ type: 'nuxtUISlider';
45
+ };
46
+ nuxtUISwitch: {
47
+ type: 'nuxtUISwitch';
48
+ };
49
+ nuxtUITextarea: {
50
+ type: 'nuxtUITextarea';
51
+ };
52
+ nuxtUIOutputText: {
53
+ type: 'nuxtUIOutputText';
54
+ };
55
+ nuxtUIOutputBoolean: {
56
+ type: 'nuxtUIOutputBoolean';
57
+ };
58
+ nuxtUIOutputDate: {
59
+ type: 'nuxtUIOutputDate';
60
+ };
61
+ nuxtUIOutputLink: {
62
+ type: 'nuxtUIOutputLink';
63
+ };
64
+ nuxtUIOutputNumber: {
65
+ type: 'nuxtUIOutputNumber';
66
+ };
67
+ nuxtUIOutputList: {
68
+ type: 'nuxtUIOutputList';
69
+ };
70
+ }
71
+ }
File without changes
@@ -1,5 +1,5 @@
1
1
  export function addNuxtAsteriskPlugin(node) {
2
- if (!node.props.type.startsWith("nuxtUI") || node.props.type.startsWith("UFOutput"))
2
+ if (!node.props.type.startsWith("nuxtUI") || node.props.type.startsWith("FUOutput"))
3
3
  return;
4
4
  node.on("created", () => {
5
5
  if (node.props.definition?.schema) {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Converts various color formats to hex
3
+ * Supports: hex, rgb, rgba, hsl, hsla, cmyk, lab
4
+ */
5
+ export declare function convertColorToHex(color: string): string;
@@ -0,0 +1,134 @@
1
+ export function convertColorToHex(color) {
2
+ if (!color)
3
+ return "#000000";
4
+ const trimmedColor = color.trim();
5
+ if (trimmedColor.startsWith("#")) {
6
+ return normalizeHex(trimmedColor);
7
+ }
8
+ if (trimmedColor.toLowerCase().startsWith("rgb")) {
9
+ return rgbToHex(trimmedColor);
10
+ }
11
+ if (trimmedColor.toLowerCase().startsWith("hsl")) {
12
+ return hslToHex(trimmedColor);
13
+ }
14
+ if (trimmedColor.toLowerCase().startsWith("cmyk")) {
15
+ return cmykToHex(trimmedColor);
16
+ }
17
+ if (trimmedColor.toLowerCase().startsWith("lab")) {
18
+ return labToHex(trimmedColor);
19
+ }
20
+ if (/^[\da-f]{3,8}$/i.test(trimmedColor)) {
21
+ return normalizeHex(`#${trimmedColor}`);
22
+ }
23
+ return "#000000";
24
+ }
25
+ function normalizeHex(hex) {
26
+ const cleaned = hex.replace("#", "").toLowerCase();
27
+ if (!/^[\da-f]+$/i.test(cleaned) || cleaned.length === 0) {
28
+ return "#000000";
29
+ }
30
+ if (cleaned.length === 3) {
31
+ return `#${cleaned[0]}${cleaned[0]}${cleaned[1]}${cleaned[1]}${cleaned[2]}${cleaned[2]}`;
32
+ }
33
+ if (cleaned.length === 4) {
34
+ return `#${cleaned[0]}${cleaned[0]}${cleaned[1]}${cleaned[1]}${cleaned[2]}${cleaned[2]}`;
35
+ }
36
+ if (cleaned.length === 8) {
37
+ return `#${cleaned.substring(0, 6)}`;
38
+ }
39
+ if (cleaned.length === 6) {
40
+ return `#${cleaned}`;
41
+ }
42
+ if (cleaned.length < 6) {
43
+ return `#${cleaned.padEnd(6, "0")}`;
44
+ }
45
+ return `#${cleaned.substring(0, 6)}`;
46
+ }
47
+ function rgbToHex(rgb) {
48
+ const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/i);
49
+ if (!match)
50
+ return "#000000";
51
+ const r = Number.parseInt(match[1], 10);
52
+ const g = Number.parseInt(match[2], 10);
53
+ const b = Number.parseInt(match[3], 10);
54
+ return `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
55
+ }
56
+ function hslToHex(hsl) {
57
+ const match = hsl.match(/hsla?\((\d+),\s*([\d.]+)%,\s*([\d.]+)%(?:,\s*[\d.]+)?\)/);
58
+ if (!match)
59
+ return "#000000";
60
+ const h = Number.parseInt(match[1], 10) / 360;
61
+ const s = Number.parseFloat(match[2]) / 100;
62
+ const l = Number.parseFloat(match[3]) / 100;
63
+ let r, g, b;
64
+ if (s === 0) {
65
+ r = g = b = l;
66
+ } else {
67
+ const hue2rgb = (p2, q2, t) => {
68
+ if (t < 0)
69
+ t += 1;
70
+ if (t > 1)
71
+ t -= 1;
72
+ if (t < 1 / 6)
73
+ return p2 + (q2 - p2) * 6 * t;
74
+ if (t < 1 / 2)
75
+ return q2;
76
+ if (t < 2 / 3)
77
+ return p2 + (q2 - p2) * (2 / 3 - t) * 6;
78
+ return p2;
79
+ };
80
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
81
+ const p = 2 * l - q;
82
+ r = hue2rgb(p, q, h + 1 / 3);
83
+ g = hue2rgb(p, q, h);
84
+ b = hue2rgb(p, q, h - 1 / 3);
85
+ }
86
+ return `#${componentToHex(Math.round(r * 255))}${componentToHex(Math.round(g * 255))}${componentToHex(Math.round(b * 255))}`;
87
+ }
88
+ function cmykToHex(cmyk) {
89
+ const match = cmyk.match(/cmyk\(([\d.]+)%?,\s*([\d.]+)%?,\s*([\d.]+)%?,\s*([\d.]+)%?\)/);
90
+ if (!match)
91
+ return "#000000";
92
+ const c = Number.parseFloat(match[1]) / 100;
93
+ const m = Number.parseFloat(match[2]) / 100;
94
+ const y = Number.parseFloat(match[3]) / 100;
95
+ const k = Number.parseFloat(match[4]) / 100;
96
+ const r = Math.round(255 * (1 - c) * (1 - k));
97
+ const g = Math.round(255 * (1 - m) * (1 - k));
98
+ const b = Math.round(255 * (1 - y) * (1 - k));
99
+ return `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
100
+ }
101
+ function labToHex(lab) {
102
+ const match = lab.match(/lab\(([\d.]+)%?,\s*([-\d.]+),\s*([-\d.]+)\)/);
103
+ if (!match)
104
+ return "#000000";
105
+ const l = Number.parseFloat(match[1]);
106
+ const a = Number.parseFloat(match[2]);
107
+ const b = Number.parseFloat(match[3]);
108
+ let y = (l + 16) / 116;
109
+ let x = a / 500 + y;
110
+ let z = y - b / 200;
111
+ const labToXyzHelper = (t) => {
112
+ return t > 0.206897 ? t ** 3 : (t - 16 / 116) / 7.787;
113
+ };
114
+ x = 95.047 * labToXyzHelper(x);
115
+ y = 100 * labToXyzHelper(y);
116
+ z = 108.883 * labToXyzHelper(z);
117
+ x = x / 100;
118
+ y = y / 100;
119
+ z = z / 100;
120
+ let r = x * 3.2406 + y * -1.5372 + z * -0.4986;
121
+ let g = x * -0.9689 + y * 1.8758 + z * 0.0415;
122
+ let bl = x * 0.0557 + y * -0.204 + z * 1.057;
123
+ const xyzToRgbHelper = (t) => {
124
+ return t > 31308e-7 ? 1.055 * t ** (1 / 2.4) - 0.055 : 12.92 * t;
125
+ };
126
+ r = xyzToRgbHelper(r);
127
+ g = xyzToRgbHelper(g);
128
+ bl = xyzToRgbHelper(bl);
129
+ return `#${componentToHex(Math.max(0, Math.min(255, Math.round(r * 255))))}${componentToHex(Math.max(0, Math.min(255, Math.round(g * 255))))}${componentToHex(Math.max(0, Math.min(255, Math.round(bl * 255))))}`;
130
+ }
131
+ function componentToHex(c) {
132
+ const hex = Math.max(0, Math.min(255, c)).toString(16);
133
+ return hex.length === 1 ? `0${hex}` : hex;
134
+ }
@@ -0,0 +1,2 @@
1
+ export declare function durationToMinutes(duration: string): number;
2
+ export declare function formattedDuration(duration: string): string;
@@ -0,0 +1,32 @@
1
+ export function durationToMinutes(duration) {
2
+ let hours = 0;
3
+ let minutes = 0;
4
+ const lowerDuration = duration.toLowerCase();
5
+ if (lowerDuration.includes(":")) {
6
+ const [h, m] = lowerDuration.split(":").map((part) => +(part?.trim() || "0"));
7
+ hours = h ?? 0;
8
+ minutes = m ?? 0;
9
+ } else {
10
+ if (lowerDuration.includes("h")) {
11
+ hours = +(lowerDuration.split("h")[0]?.trim() || "0");
12
+ const remainder = lowerDuration.split("h")[1] || "";
13
+ if (remainder.includes("m"))
14
+ minutes = +(remainder.split("m")[0]?.trim() || "0");
15
+ else if (/^\d+$/.test(remainder))
16
+ minutes = +remainder;
17
+ } else if (lowerDuration.includes("m")) {
18
+ minutes = +(lowerDuration.split("m")[0]?.trim() || "0");
19
+ } else if (/^\d+$/.test(lowerDuration)) {
20
+ minutes = +lowerDuration;
21
+ }
22
+ }
23
+ return hours * 60 + minutes;
24
+ }
25
+ export function formattedDuration(duration) {
26
+ const minutes = durationToMinutes(duration);
27
+ const hours = Math.trunc(minutes / 60);
28
+ const remainingMinutes = minutes % 60;
29
+ if (minutes === 0)
30
+ return "0";
31
+ return `${hours > 0 ? `${hours}h` : ""}${hours > 0 && remainingMinutes > 0 ? " " : ""}${remainingMinutes > 0 ? `${remainingMinutes}m` : ""}`;
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfxcode/formkit-nuxt-ui",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "FormKit integration for Nuxt UI - Seamlessly connect FormKit form handling with Nuxt UI components",
5
5
  "author": {
6
6
  "name": "Tom",
@@ -46,7 +46,8 @@
46
46
  "typesVersions": {
47
47
  "*": {
48
48
  ".": [
49
- "./dist/types.d.mts"
49
+ "./dist/types.d.mts",
50
+ "./dist/runtime/index.d.ts"
50
51
  ]
51
52
  }
52
53
  },