@shwfed/nuxt 0.11.3 → 0.11.5

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shwfed/nuxt",
3
3
  "configKey": "shwfed",
4
- "version": "0.11.3",
4
+ "version": "0.11.5",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -1,7 +1,7 @@
1
1
  import { Effect } from 'effect';
2
2
  import { type FieldsConfigInput } from './ui/fields/Fields.vue.js';
3
3
  export { CalendarFieldC, EmptyFieldC, FieldC, FieldsBodyC, FieldsBodyInputC, FieldsConfigC, FieldsConfigInputC, NumberFieldC, SelectFieldC, SlotFieldC, StringFieldC, CURRENT_COMPATIBILITY_DATE, KIND, SUPPORTED_COMPATIBILITY_DATES, createFieldsConfig, } from './ui/fields/Fields.vue.js';
4
- export type { EmptyField, Field, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, } from './ui/fields/Fields.vue.js';
4
+ export type { EmptyField, Field, FieldsInstance, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, } from './ui/fields/Fields.vue.js';
5
5
  declare const _default: typeof __VLS_export;
6
6
  export default _default;
7
7
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
2
  import { Effect } from "effect";
3
+ import { ref } from "vue";
3
4
  import UiFields, {
4
5
  createFieldsConfig
5
6
  } from "./ui/fields/Fields.vue";
@@ -23,6 +24,33 @@ function handleConfigUpdate(config2) {
23
24
  function handleInitialValueReady() {
24
25
  emit("initial-value-ready");
25
26
  }
27
+ const fieldsRef = ref(null);
28
+ defineExpose(new Proxy({}, {
29
+ get(_target, property) {
30
+ return fieldsRef.value ? Reflect.get(fieldsRef.value, property) : void 0;
31
+ },
32
+ has(_target, property) {
33
+ return fieldsRef.value ? Reflect.has(fieldsRef.value, property) : false;
34
+ },
35
+ ownKeys() {
36
+ return fieldsRef.value ? Reflect.ownKeys(fieldsRef.value) : [];
37
+ },
38
+ getOwnPropertyDescriptor(_target, property) {
39
+ if (!fieldsRef.value || !Reflect.has(fieldsRef.value, property)) {
40
+ return void 0;
41
+ }
42
+ return {
43
+ configurable: true,
44
+ enumerable: true,
45
+ get() {
46
+ if (!fieldsRef.value) {
47
+ return void 0;
48
+ }
49
+ return Reflect.get(fieldsRef.value, property);
50
+ }
51
+ };
52
+ }
53
+ }));
26
54
  </script>
27
55
 
28
56
  <script>
@@ -47,6 +75,7 @@ export {
47
75
 
48
76
  <template>
49
77
  <UiFields
78
+ ref="fieldsRef"
50
79
  v-bind="$attrs"
51
80
  v-model="modelValue"
52
81
  :config="config"
@@ -1,7 +1,7 @@
1
1
  import { Effect } from 'effect';
2
2
  import { type FieldsConfigInput } from './ui/fields/Fields.vue.js';
3
3
  export { CalendarFieldC, EmptyFieldC, FieldC, FieldsBodyC, FieldsBodyInputC, FieldsConfigC, FieldsConfigInputC, NumberFieldC, SelectFieldC, SlotFieldC, StringFieldC, CURRENT_COMPATIBILITY_DATE, KIND, SUPPORTED_COMPATIBILITY_DATES, createFieldsConfig, } from './ui/fields/Fields.vue.js';
4
- export type { EmptyField, Field, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, } from './ui/fields/Fields.vue.js';
4
+ export type { EmptyField, Field, FieldsInstance, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, } from './ui/fields/Fields.vue.js';
5
5
  declare const _default: typeof __VLS_export;
6
6
  export default _default;
7
7
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
@@ -28,17 +28,6 @@ function handleOpenUpdate(sessionId, open) {
28
28
  reason: "dismiss"
29
29
  });
30
30
  }
31
- function patchFooter(sessionId, definitionId, nextProps) {
32
- if (!definitionId) {
33
- return;
34
- }
35
- overlay.patchSync(sessionId, {
36
- footer: {
37
- id: definitionId,
38
- props: nextProps
39
- }
40
- });
41
- }
42
31
  const renderedSessions = computed(() => {
43
32
  const nextSessions = [];
44
33
  for (const session of overlay.sessions) {
@@ -46,7 +35,6 @@ const renderedSessions = computed(() => {
46
35
  if (!definition) {
47
36
  continue;
48
37
  }
49
- const footerDefinition = session.footer ? overlay.definitions[session.footer.id] : void 0;
50
38
  nextSessions.push({
51
39
  sessionId: session.sessionId,
52
40
  definitionId: session.definitionId,
@@ -59,11 +47,6 @@ const renderedSessions = computed(() => {
59
47
  },
60
48
  descriptionSrOnly: session.shell.descriptionSrOnly,
61
49
  props: session.props,
62
- footer: session.footer && footerDefinition ? {
63
- definitionId: session.footer.id,
64
- props: session.footer.props,
65
- definition: footerDefinition
66
- } : void 0,
67
50
  definition
68
51
  });
69
52
  }
@@ -115,23 +98,6 @@ const renderedSessions = computed(() => {
115
98
  close: (value) => overlay.closeSync(session.sessionId, value),
116
99
  patch: (nextProps) => overlay.patchSync(session.sessionId, { props: nextProps }),
117
100
  isDesktop: modalSlotProps.isDesktop
118
- }"
119
- />
120
- </template>
121
-
122
- <template
123
- v-if="session.footer"
124
- #footer="modalSlotProps"
125
- >
126
- <OverlayBody
127
- :render="session.footer.definition.render"
128
- :slot-props="{
129
- props: session.footer.props,
130
- shell: session.shell,
131
- sessionId: session.sessionId,
132
- close: (value) => overlay.closeSync(session.sessionId, value),
133
- patch: (nextProps) => patchFooter(session.sessionId, session.footer?.definitionId, nextProps),
134
- isDesktop: modalSlotProps.isDesktop
135
101
  }"
136
102
  />
137
103
  </template>
@@ -54,16 +54,32 @@ watch(currentConfig, (value) => {
54
54
  }, { immediate: true });
55
55
  const modalDefinitions = computed(() => {
56
56
  const nextDefinitions = [];
57
- for (const [slotName, render] of Object.entries(slots)) {
58
- if (!uuidPattern.test(slotName) || typeof render !== "function") {
59
- continue;
57
+ for (const group of displayConfig.value.groups) {
58
+ for (const item of group.items) {
59
+ if (isDropdownItem(item)) {
60
+ for (const child of item.items) {
61
+ const render2 = slots[child.id];
62
+ if (render2) {
63
+ nextDefinitions.push({
64
+ definitionId: child.id,
65
+ ownerId: overlayOwnerId,
66
+ render: render2,
67
+ shell: getModalShell(child.modal)
68
+ });
69
+ }
70
+ }
71
+ continue;
72
+ }
73
+ const render = slots[item.id];
74
+ if (render) {
75
+ nextDefinitions.push({
76
+ definitionId: item.id,
77
+ ownerId: overlayOwnerId,
78
+ render,
79
+ shell: getModalShell(item.modal)
80
+ });
81
+ }
60
82
  }
61
- nextDefinitions.push({
62
- definitionId: slotName,
63
- ownerId: overlayOwnerId,
64
- render,
65
- shell: getModalShell(findButtonAction(slotName)?.modal)
66
- });
67
83
  }
68
84
  return nextDefinitions;
69
85
  });
@@ -2,6 +2,9 @@ import { Effect } from 'effect';
2
2
  import type { CSSProperties } from 'vue';
3
3
  export { CalendarFieldC, CURRENT_COMPATIBILITY_DATE, EmptyFieldC, FieldC, FieldsBodyC, FieldsBodyInputC, FieldsConfigC, FieldsConfigInputC, KIND, NumberFieldC, SelectFieldC, SlotFieldC, SUPPORTED_COMPATIBILITY_DATES, StringFieldC, ValidationRuleC, createFieldsConfig, validationC, } from './schema.js';
4
4
  export type { EmptyField, Field, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, ValidationRule, } from './schema.js';
5
+ export type FieldsInstance = {
6
+ valid: import('effect').Effect.Effect<boolean, never>;
7
+ };
5
8
  declare const _default: typeof __VLS_export;
6
9
  export default _default;
7
10
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
@@ -127,7 +130,9 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
127
130
  }> | undefined>;
128
131
  } & {
129
132
  modelValue?: Record<string, unknown>;
130
- }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
133
+ }, {
134
+ valid: Effect.Effect<boolean, never, never>;
135
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
131
136
  "update:modelValue": (value: Record<string, unknown>) => any;
132
137
  "update:config": (args_0: Readonly<{
133
138
  fields: readonly ({
@@ -41,8 +41,10 @@ const displayConfig = ref(defaultConfig);
41
41
  const validationErrors = ref({});
42
42
  const calendarOpen = ref({});
43
43
  const selectOpen = ref({});
44
+ const isReady = ref(false);
44
45
  const hasInitializedFieldValues = ref(false);
45
46
  const hasEmittedInitialValueReady = ref(false);
47
+ const readyResolvers = [];
46
48
  function cloneConfig(config2) {
47
49
  const nextConfig = {
48
50
  kind: config2.kind,
@@ -214,6 +216,24 @@ function handleSelectCommandValueChange(field, state, value) {
214
216
  function clearFieldValidation(path) {
215
217
  Reflect.deleteProperty(validationErrors.value, path);
216
218
  }
219
+ function markReady() {
220
+ if (isReady.value) {
221
+ return;
222
+ }
223
+ isReady.value = true;
224
+ while (readyResolvers.length > 0) {
225
+ const resolve = readyResolvers.shift();
226
+ resolve?.();
227
+ }
228
+ }
229
+ function waitForReady() {
230
+ if (isReady.value) {
231
+ return Promise.resolve();
232
+ }
233
+ return new Promise((resolve) => {
234
+ readyResolvers.push(resolve);
235
+ });
236
+ }
217
237
  function snapshotValidationContext(field) {
218
238
  const form = structuredClone(toRaw(modelValue.value));
219
239
  return {
@@ -221,10 +241,9 @@ function snapshotValidationContext(field) {
221
241
  form
222
242
  };
223
243
  }
224
- function validateField(field) {
244
+ function getValidationFailure(field) {
225
245
  if (!field.validation?.length || isFieldHidden(field) || isFieldDisabled(field)) {
226
- clearFieldValidation(field.path);
227
- return;
246
+ return void 0;
228
247
  }
229
248
  const context = {
230
249
  value: getFieldValue(field),
@@ -232,14 +251,39 @@ function validateField(field) {
232
251
  };
233
252
  for (const rule of field.validation) {
234
253
  if (!$dsl.evaluate`${rule.expression}`(context)) {
235
- validationErrors.value[field.path] = {
254
+ return {
236
255
  message: rule.message,
237
256
  context: snapshotValidationContext(field)
238
257
  };
239
- return;
240
258
  }
241
259
  }
260
+ return void 0;
261
+ }
262
+ function syncFieldValidation(field) {
263
+ const failure = getValidationFailure(field);
264
+ if (failure) {
265
+ validationErrors.value[field.path] = failure;
266
+ return false;
267
+ }
242
268
  clearFieldValidation(field.path);
269
+ return true;
270
+ }
271
+ function validateField(field) {
272
+ syncFieldValidation(field);
273
+ }
274
+ function validateFields() {
275
+ const nextValidationErrors = {};
276
+ for (const field of displayConfig.value.fields) {
277
+ if (isPassiveField(field)) {
278
+ continue;
279
+ }
280
+ const failure = getValidationFailure(field);
281
+ if (failure) {
282
+ nextValidationErrors[field.path] = failure;
283
+ }
284
+ }
285
+ validationErrors.value = nextValidationErrors;
286
+ return Object.keys(nextValidationErrors).length === 0;
243
287
  }
244
288
  function isFieldInvalid(field) {
245
289
  return validationErrors.value[field.path] !== void 0;
@@ -274,6 +318,14 @@ function handleConfiguratorConfirm(nextConfig) {
274
318
  displayConfig.value = cloneConfig(nextConfig);
275
319
  emit("update:config", nextConfig);
276
320
  }
321
+ const fieldsApi = {
322
+ valid: Effect.async((resume) => {
323
+ void waitForReady().then(() => {
324
+ resume(Effect.sync(() => validateFields()));
325
+ });
326
+ })
327
+ };
328
+ defineExpose(fieldsApi);
277
329
  watch(config, (value) => {
278
330
  if (!value) {
279
331
  return;
@@ -287,6 +339,7 @@ watch(config, (value) => {
287
339
  hasEmittedInitialValueReady.value = true;
288
340
  emit("initial-value-ready");
289
341
  }
342
+ markReady();
290
343
  }, { immediate: true });
291
344
  watchEffect(() => {
292
345
  const activePaths = /* @__PURE__ */ new Set();
@@ -2,6 +2,9 @@ import { Effect } from 'effect';
2
2
  import type { CSSProperties } from 'vue';
3
3
  export { CalendarFieldC, CURRENT_COMPATIBILITY_DATE, EmptyFieldC, FieldC, FieldsBodyC, FieldsBodyInputC, FieldsConfigC, FieldsConfigInputC, KIND, NumberFieldC, SelectFieldC, SlotFieldC, SUPPORTED_COMPATIBILITY_DATES, StringFieldC, ValidationRuleC, createFieldsConfig, validationC, } from './schema.js';
4
4
  export type { EmptyField, Field, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, ValidationRule, } from './schema.js';
5
+ export type FieldsInstance = {
6
+ valid: import('effect').Effect.Effect<boolean, never>;
7
+ };
5
8
  declare const _default: typeof __VLS_export;
6
9
  export default _default;
7
10
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
@@ -127,7 +130,9 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
127
130
  }> | undefined>;
128
131
  } & {
129
132
  modelValue?: Record<string, unknown>;
130
- }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
133
+ }, {
134
+ valid: Effect.Effect<boolean, never, never>;
135
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
131
136
  "update:modelValue": (value: Record<string, unknown>) => any;
132
137
  "update:config": (args_0: Readonly<{
133
138
  fields: readonly ({
@@ -14,19 +14,13 @@ export type OverlayShellProps = Readonly<{
14
14
  dismissible?: boolean;
15
15
  }>;
16
16
  export type OverlayBodyProps = Readonly<Record<string, unknown>>;
17
- export type OverlaySectionReferenceInput = Readonly<{
18
- id: string;
19
- props?: Record<string, unknown>;
20
- }>;
21
17
  export type OverlaySessionInput = Readonly<{
22
18
  shell?: Partial<OverlayShellProps>;
23
19
  props?: Record<string, unknown>;
24
- footer?: OverlaySectionReferenceInput;
25
20
  }>;
26
21
  export type OverlaySessionPatch = Readonly<{
27
22
  shell?: Partial<OverlayShellProps>;
28
23
  props?: Record<string, unknown>;
29
- footer?: OverlaySectionReferenceInput | null;
30
24
  }>;
31
25
  export type OverlaySlotProps = Readonly<{
32
26
  props: OverlayBodyProps;
@@ -51,10 +45,6 @@ type OverlaySession = {
51
45
  definitionId: string;
52
46
  shell: OverlayShellState;
53
47
  props: Record<string, unknown>;
54
- footer?: {
55
- id: string;
56
- props: Record<string, unknown>;
57
- };
58
48
  deferred: Deferred.Deferred<OverlayResult, never>;
59
49
  };
60
50
  export type OverlayHandle = Readonly<{
@@ -112,18 +112,6 @@ function createOverlayRuntime() {
112
112
  ...patch.props
113
113
  };
114
114
  }
115
- if (Reflect.has(patch, "footer")) {
116
- if (patch.footer === null || patch.footer === void 0) {
117
- session.footer = void 0;
118
- return;
119
- }
120
- session.footer = {
121
- id: patch.footer.id,
122
- props: {
123
- ...patch.footer.props
124
- }
125
- };
126
- }
127
115
  }
128
116
  function closeSync(sessionId, value) {
129
117
  void Effect.runPromise(closeInternal(sessionId, value));
@@ -175,9 +163,6 @@ function createOverlayRuntime() {
175
163
  if (!definition) {
176
164
  return yield* Effect.fail(createDefinitionNotFoundError(definitionId));
177
165
  }
178
- if (options?.footer && !getDefinition(options.footer.id)) {
179
- return yield* Effect.fail(createDefinitionNotFoundError(options.footer.id));
180
- }
181
166
  const buttonActionOption = yield* Effect.serviceOption(ButtonActionService);
182
167
  const buttonAction = Option.match(buttonActionOption, {
183
168
  onNone: () => void 0,
@@ -206,12 +191,6 @@ function createOverlayRuntime() {
206
191
  props: {
207
192
  ...options?.props
208
193
  },
209
- footer: options?.footer ? {
210
- id: options.footer.id,
211
- props: {
212
- ...options.footer.props
213
- }
214
- } : void 0,
215
194
  deferred
216
195
  };
217
196
  sessions.value.push(session);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shwfed/nuxt",
3
- "version": "0.11.3",
3
+ "version": "0.11.5",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "type": "module",