@hybridly/vue 0.3.0 → 0.4.0

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/index.cjs CHANGED
@@ -280,14 +280,12 @@ async function initializeHybridly(options = {}) {
280
280
  if (!element) {
281
281
  throw new Error("Could not find an HTML element to initialize Vue on.");
282
282
  }
283
- if (!payload) {
284
- throw new Error("No payload. Are you using `@hybridly` or the `payload` option?");
285
- }
286
283
  state.setContext(await core.createRouter({
287
284
  axios: resolved.axios,
288
285
  plugins: resolved.plugins,
289
286
  serializer: resolved.serializer,
290
287
  responseErrorModals: resolved.responseErrorModals ?? process.env.NODE_ENV === "development",
288
+ routing: resolved.routing,
291
289
  adapter: {
292
290
  resolveComponent: resolve,
293
291
  onWaitingForMount: (callback) => {
@@ -346,21 +344,20 @@ function prepare(options) {
346
344
  const id = options.id ?? "root";
347
345
  const element = document?.getElementById(id) ?? void 0;
348
346
  utils.debug.adapter("vue", `Element "${id}" is:`, element);
349
- const payload = options.payload ?? element?.dataset.payload ? JSON.parse(element.dataset.payload) : void 0;
347
+ const payload = element?.dataset.payload ? JSON.parse(element.dataset.payload) : void 0;
348
+ if (!payload) {
349
+ throw new Error("No payload found. Are you using the `@hybridly` directive?");
350
+ }
350
351
  if (options.cleanup !== false) {
351
352
  delete element.dataset.payload;
352
353
  }
353
354
  utils.debug.adapter("vue", "Resolved:", { isServer, element, payload });
354
355
  const resolve = async (name) => {
355
356
  utils.debug.adapter("vue", "Resolving component", name);
356
- if (options.resolve) {
357
- const component = await options.resolve?.(name);
358
- return component.default ?? component;
357
+ if (!options.imported) {
358
+ throw new Error("No component loaded. Did you initialize Hybridly? Does `php artisan hybridly:config` return an error?");
359
359
  }
360
- if (options.components) {
361
- return await resolvePageComponent(name, options);
362
- }
363
- throw new Error("Either `initializeHybridly#resolve` or `initializeHybridly#pages` should be defined.");
360
+ return await resolveViewComponent(name, options);
364
361
  };
365
362
  if (options.progress !== false) {
366
363
  options.plugins = [
@@ -375,21 +372,13 @@ function prepare(options) {
375
372
  resolve
376
373
  };
377
374
  }
378
- async function resolvePageComponent(name, options) {
379
- const components = options.components;
380
- if (name.includes(":")) {
381
- if (options.domains === false) {
382
- utils.showDomainsDisabledErrorModal(name);
383
- console.warn(`${name} is a domain-based component, but domains are disabled.`);
384
- return;
385
- }
386
- const [domain, page] = name.split(":");
387
- name = `${options.domains}.${domain}.${options.pages}.${page}`;
388
- }
389
- const path = Object.keys(components).sort((a, b) => a.length - b.length).find((path2) => path2.endsWith(`${name.replaceAll(".", "/")}.vue`));
390
- if (!path) {
375
+ async function resolveViewComponent(name, options) {
376
+ const components = options.imported;
377
+ const result = options.components.views.find((view) => name === view.identifier);
378
+ const path = Object.keys(components).sort((a, b) => a.length - b.length).find((path2) => result ? path2.endsWith(result?.path) : false);
379
+ if (!result || !path) {
380
+ console.warn(`Page component [${name}] not found. Available components: `, options.components.views.map(({ identifier }) => identifier));
391
381
  utils.showPageComponentErrorModal(name);
392
- console.warn(`Page component "${name}" could not be found. Available pages:`, Object.keys(components));
393
382
  return;
394
383
  }
395
384
  let component = typeof components[path] === "function" ? await components[path]() : components[path];
@@ -645,15 +634,15 @@ function useForm(options) {
645
634
  if (keys.length === 0) {
646
635
  return isDirty.value;
647
636
  }
648
- return keys.some((key) => !isEqual__default(vue.toRaw(fields[key]), vue.toRaw(initial[key])));
637
+ return keys.some((key) => !isEqual__default(vue.toRaw(dotDiver.getByPath(fields, key)), vue.toRaw(dotDiver.getByPath(initial, key))));
649
638
  }
650
639
  function clearError(key) {
651
- delete errors.value[key];
640
+ utils.unsetPropertyAtPath(errors.value, key);
652
641
  }
653
642
  function setErrors(incoming) {
654
643
  clearErrors();
655
- Object.entries(incoming).forEach(([key, value]) => {
656
- errors.value[key] = value;
644
+ Object.entries(incoming).forEach(([path, value]) => {
645
+ utils.setValueAtPath(errors.value, path, value);
657
646
  });
658
647
  }
659
648
  function abort() {
@@ -680,7 +669,7 @@ function useForm(options) {
680
669
  hasDirty,
681
670
  submitWithOptions: submit,
682
671
  submit: () => submit(),
683
- hasErrors: vue.computed(() => Object.values(errors.value).length > 0),
672
+ hasErrors: vue.computed(() => Object.values(errors.value ?? {}).length > 0),
684
673
  initial,
685
674
  loaded,
686
675
  progress,
@@ -885,7 +874,6 @@ exports.defineLayout = defineLayout;
885
874
  exports.defineLayoutProperties = defineLayoutProperties;
886
875
  exports.initializeHybridly = initializeHybridly;
887
876
  exports.registerHook = registerHook;
888
- exports.resolvePageComponent = resolvePageComponent;
889
877
  exports.useBackForward = useBackForward;
890
878
  exports.useContext = useContext;
891
879
  exports.useDialog = useDialog;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import * as vue from 'vue';
2
2
  import { App, Plugin as Plugin$2, h, PropType, ComputedRef, DeepReadonly } from 'vue';
3
3
  import * as _hybridly_core from '@hybridly/core';
4
- import { ResolveComponent as ResolveComponent$1, RouterContextOptions, Plugin as Plugin$1, HybridPayload as HybridPayload$1, RouterContext, Method as Method$1, HybridRequestOptions as HybridRequestOptions$1, UrlResolvable as UrlResolvable$1, registerHook as registerHook$1 } from '@hybridly/core';
4
+ import { RouterContextOptions, Plugin as Plugin$1, RouterContext, Method as Method$1, HybridRequestOptions as HybridRequestOptions$1, UrlResolvable as UrlResolvable$1, registerHook as registerHook$1 } from '@hybridly/core';
5
5
  export { can, route, router } from '@hybridly/core';
6
6
  import { Axios, AxiosResponse, AxiosProgressEvent } from 'axios';
7
- import { HybridlyConfig } from '@hybridly/config';
8
7
  import { ProgressOptions } from '@hybridly/progress-plugin';
9
8
  import * as _vue_shared from '@vue/shared';
10
9
  import { RequestData } from '@hybridly/utils';
@@ -14,11 +13,6 @@ import { SearchableObject, Path, PathValue } from '@clickbar/dot-diver';
14
13
  * Initializes Hybridly's router and context.
15
14
  */
16
15
  declare function initializeHybridly(options?: InitializeOptions): Promise<any>;
17
- /**
18
- * Resolves a page component.
19
- */
20
- declare function resolvePageComponent(name: string, options: ResolvedInitializeOptions): Promise<any>;
21
- type ResolvedInitializeOptions = InitializeOptions & HybridlyConfig;
22
16
  interface InitializeOptions {
23
17
  /** Callback that gets executed before Vue is mounted. */
24
18
  enhanceVue?: (vue: App<Element>) => any;
@@ -30,8 +24,6 @@ interface InitializeOptions {
30
24
  devtools?: boolean;
31
25
  /** Whether to display response error modals. */
32
26
  responseErrorModals?: boolean;
33
- /** A custom component resolution option. */
34
- resolve?: ResolveComponent$1;
35
27
  /** Custom history state serialization functions. */
36
28
  serializer?: RouterContextOptions['serializer'];
37
29
  /** Progressbar options. */
@@ -42,10 +34,6 @@ interface InitializeOptions {
42
34
  plugins?: Plugin$1[];
43
35
  /** Custom Axios instance. */
44
36
  axios?: Axios;
45
- /** Initial view data. This is automatically set by Laravel, using this option would override the default behavior. */
46
- payload?: HybridPayload$1;
47
- /** A custom collection of pages components. This is automatically determined thanks to `root` and `pages`, using this would override the default behavior. */
48
- components?: Record<string, any>;
49
37
  }
50
38
  interface SetupArguments {
51
39
  /** DOM element to mount Vue on. */
@@ -97,42 +85,16 @@ declare const RouterLink: vue.DefineComponent<{
97
85
  required: false;
98
86
  default: undefined;
99
87
  };
100
- }, (props: Readonly<_vue_shared.LooseRequired<Readonly<vue.ExtractPropTypes<{
101
- href: {
102
- type: StringConstructor;
103
- required: false;
104
- default: undefined;
105
- };
106
- as: {
107
- type: (ObjectConstructor | StringConstructor)[];
108
- default: string;
109
- };
110
- method: {
111
- type: PropType<"delete" | Method$1 | "get" | "post" | "put" | "patch">;
112
- default: string;
113
- };
114
- data: {
115
- type: PropType<RequestData>;
116
- default: () => {};
117
- };
118
- external: {
119
- type: BooleanConstructor;
120
- default: boolean;
121
- };
122
- disabled: {
123
- type: BooleanConstructor;
124
- default: boolean;
125
- };
126
- options: {
127
- type: PropType<Omit<HybridRequestOptions$1, "data" | "url" | "method">>;
128
- default: () => {};
129
- };
130
- text: {
131
- type: StringConstructor;
132
- required: false;
133
- default: undefined;
134
- };
135
- }>> & {}>>) => vue.VNode<vue.RendererNode, vue.RendererElement, {
88
+ }, (props: _vue_shared.LooseRequired<{
89
+ readonly data: RequestData;
90
+ readonly method: "delete" | Method$1 | "get" | "post" | "put" | "patch";
91
+ readonly options: Omit<HybridRequestOptions$1, "data" | "url" | "method">;
92
+ readonly as: string | Record<string, any>;
93
+ readonly external: boolean;
94
+ readonly disabled: boolean;
95
+ readonly text?: string | undefined;
96
+ readonly href?: string | undefined;
97
+ } & {}>) => vue.VNode<vue.RendererNode, vue.RendererElement, {
136
98
  [key: string]: any;
137
99
  }>, unknown, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.VNodeProps & vue.AllowedComponentProps & vue.ComponentCustomProps, Readonly<vue.ExtractPropTypes<{
138
100
  href: {
@@ -178,7 +140,7 @@ declare const RouterLink: vue.DefineComponent<{
178
140
  as: string | Record<string, any>;
179
141
  external: boolean;
180
142
  disabled: boolean;
181
- }>;
143
+ }, {}>;
182
144
 
183
145
  /** Accesses all current properties. */
184
146
  declare function useProperties<T extends object, Global extends GlobalHybridlyProperties>(): vue.DeepReadonly<vue.UnwrapNestedRefs<T & Global>>;
@@ -218,7 +180,7 @@ interface RequestHooks {
218
180
  /**
219
181
  * Called when a request is successful but there were errors.
220
182
  */
221
- error: (errors: Errors, context: InternalRouterContext) => MaybePromise<any>;
183
+ error: (errors: Errors$1, context: InternalRouterContext) => MaybePromise<any>;
222
184
  /**
223
185
  * Called when a request has been aborted.
224
186
  */
@@ -406,9 +368,7 @@ interface Progress {
406
368
  /** Computed percentage. */
407
369
  percentage: Readonly<number>;
408
370
  }
409
- interface Errors {
410
- [key: string]: string;
411
- }
371
+ type Errors$1 = any;
412
372
 
413
373
  interface Plugin extends Partial<Hooks> {
414
374
  /** Identifier of the plugin. */
@@ -475,8 +435,10 @@ interface Serializer {
475
435
  /** Accesses the hybridly context. */
476
436
  declare function useContext(): vue.ComputedRef<Readonly<InternalRouterContext> | undefined>;
477
437
 
478
- type Fields = Record<string, any>;
479
- interface FormOptions<T extends Fields> extends Omit<HybridRequestOptions$1, 'data' | 'url'> {
438
+ type Errors<T extends SearchableObject> = {
439
+ [K in keyof T]?: T[K] extends Record<string, any> ? Errors<T[K]> : string;
440
+ };
441
+ interface FormOptions<T extends SearchableObject> extends Omit<HybridRequestOptions$1, 'data' | 'url'> {
480
442
  fields: T;
481
443
  url?: UrlResolvable$1 | (() => UrlResolvable$1);
482
444
  key?: string | false;
@@ -497,18 +459,18 @@ interface FormOptions<T extends Fields> extends Omit<HybridRequestOptions$1, 'da
497
459
  /**
498
460
  * Callback executed before the form submission for transforming the fields.
499
461
  */
500
- transform?: (fields: T) => Fields;
462
+ transform?: (fields: T) => any;
501
463
  }
502
- declare function useForm<T extends Fields = Fields>(options: FormOptions<T>): {
503
- reset: (...keys: (keyof T)[]) => void;
504
- clear: (...keys: (keyof T)[]) => void;
505
- fields: vue.UnwrapRef<vue.UnwrapNestedRefs<T>>;
464
+ declare function useForm<T extends SearchableObject, P extends Path<T> & string = Path<T> & string>(options: FormOptions<T>): {
465
+ reset: (...keys: P[]) => void;
466
+ clear: (...keys: P[]) => void;
467
+ fields: vue.UnwrapRef<T>;
506
468
  abort: () => void;
507
- setErrors: (incoming: Record<string, string>) => void;
508
- clearErrors: (...keys: (keyof T)[]) => void;
509
- clearError: (key: keyof T) => void;
469
+ setErrors: (incoming: Errors<T>) => void;
470
+ clearErrors: (...keys: P[]) => void;
471
+ clearError: (key: P) => void;
510
472
  setInitial: (newInitial: Partial<T>) => void;
511
- hasDirty: (...keys: (keyof T)[]) => boolean;
473
+ hasDirty: (...keys: P[]) => boolean;
512
474
  submitWithOptions: (optionsOverrides?: Omit<HybridRequestOptions$1, 'data'>) => Promise<_hybridly_core.NavigationResponse>;
513
475
  submit: () => Promise<_hybridly_core.NavigationResponse>;
514
476
  hasErrors: boolean;
@@ -529,7 +491,7 @@ declare function useForm<T extends Fields = Fields>(options: FormOptions<T>): {
529
491
  readonly percentage: number;
530
492
  } | undefined;
531
493
  isDirty: boolean;
532
- errors: vue.UnwrapRef<DeepReadonly<[Record<keyof T, string>] extends [vue.Ref<any>] ? vue.Ref<any> & Record<keyof T, string> : vue.Ref<vue.UnwrapRef<Record<keyof T, string>>>>>;
494
+ errors: DeepReadonly<vue.UnwrapRef<Errors<T>>>;
533
495
  processing: boolean;
534
496
  successful: boolean;
535
497
  failed: boolean;
@@ -817,4 +779,4 @@ declare function useRefinements<Properties extends object, RefinementsKey extend
817
779
  applyFilter: (filter: string, value: any, options?: AvailableHybridRequestOptions) => Promise<_hybridly_core.NavigationResponse | undefined>;
818
780
  };
819
781
 
820
- export { Layout, RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, registerHook, resolvePageComponent, useBackForward, useContext, useDialog, useForm, useHistoryState, usePaginator, useProperties, useProperty, useRefinements };
782
+ export { Layout, RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, registerHook, useBackForward, useContext, useDialog, useForm, useHistoryState, usePaginator, useProperties, useProperty, useRefinements };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { shallowRef, ref, unref, triggerRef, defineComponent, toRaw, h, nextTick, createApp, isRef, reactive, readonly, computed, watch, getCurrentInstance, onUnmounted } from 'vue';
2
2
  import { registerHook as registerHook$1, createRouter, makeUrl, router } from '@hybridly/core';
3
3
  export { can, route, router } from '@hybridly/core';
4
- import { debug, random, showDomainsDisabledErrorModal, showPageComponentErrorModal, merge, clone } from '@hybridly/utils';
4
+ import { debug, random, showPageComponentErrorModal, merge, clone, unsetPropertyAtPath, setValueAtPath } from '@hybridly/utils';
5
5
  import { progress } from '@hybridly/progress-plugin';
6
6
  import { setupDevtoolsPlugin } from '@vue/devtools-api';
7
7
  import qs from 'qs';
@@ -272,14 +272,12 @@ async function initializeHybridly(options = {}) {
272
272
  if (!element) {
273
273
  throw new Error("Could not find an HTML element to initialize Vue on.");
274
274
  }
275
- if (!payload) {
276
- throw new Error("No payload. Are you using `@hybridly` or the `payload` option?");
277
- }
278
275
  state.setContext(await createRouter({
279
276
  axios: resolved.axios,
280
277
  plugins: resolved.plugins,
281
278
  serializer: resolved.serializer,
282
279
  responseErrorModals: resolved.responseErrorModals ?? process.env.NODE_ENV === "development",
280
+ routing: resolved.routing,
283
281
  adapter: {
284
282
  resolveComponent: resolve,
285
283
  onWaitingForMount: (callback) => {
@@ -338,21 +336,20 @@ function prepare(options) {
338
336
  const id = options.id ?? "root";
339
337
  const element = document?.getElementById(id) ?? void 0;
340
338
  debug.adapter("vue", `Element "${id}" is:`, element);
341
- const payload = options.payload ?? element?.dataset.payload ? JSON.parse(element.dataset.payload) : void 0;
339
+ const payload = element?.dataset.payload ? JSON.parse(element.dataset.payload) : void 0;
340
+ if (!payload) {
341
+ throw new Error("No payload found. Are you using the `@hybridly` directive?");
342
+ }
342
343
  if (options.cleanup !== false) {
343
344
  delete element.dataset.payload;
344
345
  }
345
346
  debug.adapter("vue", "Resolved:", { isServer, element, payload });
346
347
  const resolve = async (name) => {
347
348
  debug.adapter("vue", "Resolving component", name);
348
- if (options.resolve) {
349
- const component = await options.resolve?.(name);
350
- return component.default ?? component;
349
+ if (!options.imported) {
350
+ throw new Error("No component loaded. Did you initialize Hybridly? Does `php artisan hybridly:config` return an error?");
351
351
  }
352
- if (options.components) {
353
- return await resolvePageComponent(name, options);
354
- }
355
- throw new Error("Either `initializeHybridly#resolve` or `initializeHybridly#pages` should be defined.");
352
+ return await resolveViewComponent(name, options);
356
353
  };
357
354
  if (options.progress !== false) {
358
355
  options.plugins = [
@@ -367,21 +364,13 @@ function prepare(options) {
367
364
  resolve
368
365
  };
369
366
  }
370
- async function resolvePageComponent(name, options) {
371
- const components = options.components;
372
- if (name.includes(":")) {
373
- if (options.domains === false) {
374
- showDomainsDisabledErrorModal(name);
375
- console.warn(`${name} is a domain-based component, but domains are disabled.`);
376
- return;
377
- }
378
- const [domain, page] = name.split(":");
379
- name = `${options.domains}.${domain}.${options.pages}.${page}`;
380
- }
381
- const path = Object.keys(components).sort((a, b) => a.length - b.length).find((path2) => path2.endsWith(`${name.replaceAll(".", "/")}.vue`));
382
- if (!path) {
367
+ async function resolveViewComponent(name, options) {
368
+ const components = options.imported;
369
+ const result = options.components.views.find((view) => name === view.identifier);
370
+ const path = Object.keys(components).sort((a, b) => a.length - b.length).find((path2) => result ? path2.endsWith(result?.path) : false);
371
+ if (!result || !path) {
372
+ console.warn(`Page component [${name}] not found. Available components: `, options.components.views.map(({ identifier }) => identifier));
383
373
  showPageComponentErrorModal(name);
384
- console.warn(`Page component "${name}" could not be found. Available pages:`, Object.keys(components));
385
374
  return;
386
375
  }
387
376
  let component = typeof components[path] === "function" ? await components[path]() : components[path];
@@ -637,15 +626,15 @@ function useForm(options) {
637
626
  if (keys.length === 0) {
638
627
  return isDirty.value;
639
628
  }
640
- return keys.some((key) => !isEqual(toRaw(fields[key]), toRaw(initial[key])));
629
+ return keys.some((key) => !isEqual(toRaw(getByPath(fields, key)), toRaw(getByPath(initial, key))));
641
630
  }
642
631
  function clearError(key) {
643
- delete errors.value[key];
632
+ unsetPropertyAtPath(errors.value, key);
644
633
  }
645
634
  function setErrors(incoming) {
646
635
  clearErrors();
647
- Object.entries(incoming).forEach(([key, value]) => {
648
- errors.value[key] = value;
636
+ Object.entries(incoming).forEach(([path, value]) => {
637
+ setValueAtPath(errors.value, path, value);
649
638
  });
650
639
  }
651
640
  function abort() {
@@ -672,7 +661,7 @@ function useForm(options) {
672
661
  hasDirty,
673
662
  submitWithOptions: submit,
674
663
  submit: () => submit(),
675
- hasErrors: computed(() => Object.values(errors.value).length > 0),
664
+ hasErrors: computed(() => Object.values(errors.value ?? {}).length > 0),
676
665
  initial,
677
666
  loaded,
678
667
  progress,
@@ -869,4 +858,4 @@ function useRefinements(properties, refinementsKeys, defaultOptions = {}) {
869
858
  };
870
859
  }
871
860
 
872
- export { RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, registerHook, resolvePageComponent, useBackForward, useContext, useDialog, useForm, useHistoryState, usePaginator, useProperties, useProperty, useRefinements };
861
+ export { RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, registerHook, useBackForward, useContext, useDialog, useForm, useHistoryState, usePaginator, useProperties, useProperty, useRefinements };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridly/vue",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Vue adapter for Hybridly",
5
5
  "keywords": [
6
6
  "hybridly",
@@ -43,18 +43,17 @@
43
43
  "defu": "^6.1.2",
44
44
  "lodash.isequal": "^4.5.0",
45
45
  "nprogress": "^0.2.0",
46
- "qs": "^6.11.1",
47
- "@hybridly/config": "0.3.0",
48
- "@hybridly/core": "0.3.0",
49
- "@hybridly/progress-plugin": "0.3.0",
50
- "@hybridly/utils": "0.3.0"
46
+ "qs": "^6.11.2",
47
+ "@hybridly/core": "0.4.0",
48
+ "@hybridly/utils": "0.4.0",
49
+ "@hybridly/progress-plugin": "0.4.0"
51
50
  },
52
51
  "devDependencies": {
53
- "@types/lodash": "^4.14.194",
52
+ "@types/lodash": "^4.14.195",
54
53
  "@types/lodash.clonedeep": "^4.5.7",
55
54
  "@types/lodash.isequal": "^4.5.6",
56
55
  "@types/nprogress": "^0.2.0",
57
- "vue": "^3.2.47"
56
+ "vue": "^3.3.4"
58
57
  },
59
58
  "scripts": {
60
59
  "build": "unbuild",