@hybridly/vue 0.2.1 → 0.3.1

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
@@ -645,15 +645,15 @@ function useForm(options) {
645
645
  if (keys.length === 0) {
646
646
  return isDirty.value;
647
647
  }
648
- return keys.some((key) => !isEqual__default(vue.toRaw(fields[key]), vue.toRaw(initial[key])));
648
+ return keys.some((key) => !isEqual__default(vue.toRaw(dotDiver.getByPath(fields, key)), vue.toRaw(dotDiver.getByPath(initial, key))));
649
649
  }
650
650
  function clearError(key) {
651
- delete errors.value[key];
651
+ utils.unsetPropertyAtPath(errors.value, key);
652
652
  }
653
653
  function setErrors(incoming) {
654
654
  clearErrors();
655
- Object.entries(incoming).forEach(([key, value]) => {
656
- errors.value[key] = value;
655
+ Object.entries(incoming).forEach(([path, value]) => {
656
+ utils.setValueAtPath(errors.value, path, value);
657
657
  });
658
658
  }
659
659
  function abort() {
@@ -777,6 +777,106 @@ function useDialog() {
777
777
  };
778
778
  }
779
779
 
780
+ function useRefinements(properties, refinementsKeys, defaultOptions = {}) {
781
+ const refinements = vue.computed(() => properties[refinementsKeys]);
782
+ const sortsKey = vue.computed(() => refinements.value.keys.sorts);
783
+ const filtersKey = vue.computed(() => refinements.value.keys.filters);
784
+ async function reset(options = {}) {
785
+ return await core.router.reload({
786
+ ...defaultOptions,
787
+ ...options,
788
+ data: {
789
+ [filtersKey.value]: void 0,
790
+ [sortsKey.value]: void 0
791
+ }
792
+ });
793
+ }
794
+ async function clearFilters(options = {}) {
795
+ return await core.router.reload({
796
+ ...defaultOptions,
797
+ ...options,
798
+ data: {
799
+ [filtersKey.value]: void 0
800
+ }
801
+ });
802
+ }
803
+ async function clearFilter(filter, options = {}) {
804
+ return await core.router.reload({
805
+ ...defaultOptions,
806
+ ...options,
807
+ data: {
808
+ [filtersKey.value]: {
809
+ [filter]: void 0
810
+ }
811
+ }
812
+ });
813
+ }
814
+ async function applyFilter(filter, value, options = {}) {
815
+ if (!refinements.value.filters.find(({ name }) => name === filter)) {
816
+ return;
817
+ }
818
+ return await core.router.reload({
819
+ ...defaultOptions,
820
+ ...options,
821
+ data: {
822
+ [filtersKey.value]: {
823
+ [filter]: value === "" ? void 0 : value
824
+ }
825
+ }
826
+ });
827
+ }
828
+ async function clearSorts(options = {}) {
829
+ return await core.router.reload({
830
+ ...defaultOptions,
831
+ ...options,
832
+ data: {
833
+ [sortsKey.value]: void 0
834
+ }
835
+ });
836
+ }
837
+ function currentSorts() {
838
+ return refinements.value.sorts.filter(({ is_active }) => is_active);
839
+ }
840
+ function currentFilters() {
841
+ return refinements.value.filters.filter(({ is_active }) => is_active);
842
+ }
843
+ function isSorting() {
844
+ return currentSorts().length !== 0;
845
+ }
846
+ function isFiltering() {
847
+ return currentFilters().length !== 0;
848
+ }
849
+ async function toggleSort(sortName, options) {
850
+ const sort = refinements.value.sorts.find(({ name }) => name === sortName);
851
+ if (!sort) {
852
+ console.warn(`[Refinement] Sort "${sortName} does not exist."`);
853
+ return;
854
+ }
855
+ const next = options?.direction ? sort[options?.direction] : sort.next;
856
+ return await core.router.reload({
857
+ ...defaultOptions,
858
+ ...options,
859
+ data: {
860
+ [sortsKey.value]: next || void 0
861
+ }
862
+ });
863
+ }
864
+ return {
865
+ filters: toReactive(refinements.value.filters),
866
+ sorts: toReactive(refinements.value.sorts),
867
+ reset,
868
+ toggleSort,
869
+ isSorting,
870
+ isFiltering,
871
+ currentSorts,
872
+ currentFilters,
873
+ clearFilter,
874
+ clearSorts,
875
+ clearFilters,
876
+ applyFilter
877
+ };
878
+ }
879
+
780
880
  exports.can = core.can;
781
881
  exports.route = core.route;
782
882
  exports.router = core.router;
@@ -794,3 +894,4 @@ exports.useHistoryState = useHistoryState;
794
894
  exports.usePaginator = usePaginator;
795
895
  exports.useProperties = useProperties;
796
896
  exports.useProperty = useProperty;
897
+ exports.useRefinements = useRefinements;
package/dist/index.d.ts CHANGED
@@ -218,7 +218,7 @@ interface RequestHooks {
218
218
  /**
219
219
  * Called when a request is successful but there were errors.
220
220
  */
221
- error: (errors: Errors, context: InternalRouterContext) => MaybePromise<any>;
221
+ error: (errors: Errors$1, context: InternalRouterContext) => MaybePromise<any>;
222
222
  /**
223
223
  * Called when a request has been aborted.
224
224
  */
@@ -406,9 +406,7 @@ interface Progress {
406
406
  /** Computed percentage. */
407
407
  percentage: Readonly<number>;
408
408
  }
409
- interface Errors {
410
- [key: string]: string;
411
- }
409
+ type Errors$1 = any;
412
410
 
413
411
  interface Plugin extends Partial<Hooks> {
414
412
  /** Identifier of the plugin. */
@@ -475,8 +473,10 @@ interface Serializer {
475
473
  /** Accesses the hybridly context. */
476
474
  declare function useContext(): vue.ComputedRef<Readonly<InternalRouterContext> | undefined>;
477
475
 
478
- type Fields = Record<string, any>;
479
- interface FormOptions<T extends Fields> extends Omit<HybridRequestOptions$1, 'data' | 'url'> {
476
+ type Errors<T extends SearchableObject> = {
477
+ [K in keyof T]?: T[K] extends Record<string, any> ? Errors<T[K]> : string;
478
+ };
479
+ interface FormOptions<T extends SearchableObject> extends Omit<HybridRequestOptions$1, 'data' | 'url'> {
480
480
  fields: T;
481
481
  url?: UrlResolvable$1 | (() => UrlResolvable$1);
482
482
  key?: string | false;
@@ -497,18 +497,18 @@ interface FormOptions<T extends Fields> extends Omit<HybridRequestOptions$1, 'da
497
497
  /**
498
498
  * Callback executed before the form submission for transforming the fields.
499
499
  */
500
- transform?: (fields: T) => Fields;
500
+ transform?: (fields: T) => any;
501
501
  }
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>>;
502
+ declare function useForm<T extends SearchableObject, P extends Path<T> & string = Path<T> & string>(options: FormOptions<T>): {
503
+ reset: (...keys: P[]) => void;
504
+ clear: (...keys: P[]) => void;
505
+ fields: vue.UnwrapRef<T>;
506
506
  abort: () => void;
507
- setErrors: (incoming: Record<string, string>) => void;
508
- clearErrors: (...keys: (keyof T)[]) => void;
509
- clearError: (key: keyof T) => void;
507
+ setErrors: (incoming: Errors<T>) => void;
508
+ clearErrors: (...keys: P[]) => void;
509
+ clearError: (key: P) => void;
510
510
  setInitial: (newInitial: Partial<T>) => void;
511
- hasDirty: (...keys: (keyof T)[]) => boolean;
511
+ hasDirty: (...keys: P[]) => boolean;
512
512
  submitWithOptions: (optionsOverrides?: Omit<HybridRequestOptions$1, 'data'>) => Promise<_hybridly_core.NavigationResponse>;
513
513
  submit: () => Promise<_hybridly_core.NavigationResponse>;
514
514
  hasErrors: boolean;
@@ -529,7 +529,7 @@ declare function useForm<T extends Fields = Fields>(options: FormOptions<T>): {
529
529
  readonly percentage: number;
530
530
  } | undefined;
531
531
  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>>>>>;
532
+ errors: vue.UnwrapRef<DeepReadonly<[Errors<T>] extends [vue.Ref<any>] ? vue.Ref<any> & Errors<T> : vue.Ref<vue.UnwrapRef<Errors<T>>>>>;
533
533
  processing: boolean;
534
534
  successful: boolean;
535
535
  failed: boolean;
@@ -658,4 +658,163 @@ declare function useDialog(): {
658
658
  properties: vue.ComputedRef<Properties | undefined>;
659
659
  };
660
660
 
661
- export { Layout, RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, registerHook, resolvePageComponent, useBackForward, useContext, useDialog, useForm, useHistoryState, usePaginator, useProperties, useProperty };
661
+ type SortDirection = 'asc' | 'desc';
662
+ type AvailableHybridRequestOptions = Omit<HybridRequestOptions$1, 'url' | 'data'>;
663
+ interface ToggleSortOptions extends AvailableHybridRequestOptions {
664
+ direction?: SortDirection;
665
+ }
666
+ declare global {
667
+ interface FilterRefinement {
668
+ /**
669
+ * Whether this filter is currently active.
670
+ */
671
+ is_active: boolean;
672
+ /**
673
+ * The type of this filter.
674
+ */
675
+ type: 'trashed' | 'callback' | 'exact' | 'similarity:loose' | 'similarity:begins_with_strict' | 'similarity:ends_with_strict' | string;
676
+ /**
677
+ * The label of the filter.
678
+ */
679
+ label: string;
680
+ /**
681
+ * The metadata attributes of the filter.
682
+ */
683
+ metadata: Record<string, any>;
684
+ /**
685
+ * The name of the fitler.
686
+ */
687
+ name: string;
688
+ /**
689
+ * The current value of the filter.
690
+ */
691
+ value: any;
692
+ /**
693
+ * Whether this filter is hidden.
694
+ */
695
+ hidden: boolean;
696
+ }
697
+ interface SortRefinement {
698
+ /**
699
+ * Whether this sort is currently active.
700
+ */
701
+ is_active: boolean;
702
+ /**
703
+ * The current direction of the sort.
704
+ */
705
+ direction?: SortDirection;
706
+ /**
707
+ * The default direction of the sort.
708
+ */
709
+ default?: SortDirection;
710
+ /**
711
+ * The label of the sort.
712
+ */
713
+ label: string;
714
+ /**
715
+ * The metadata attributes of the sort.
716
+ */
717
+ metadata: Record<string, any>;
718
+ /**
719
+ * The name of the sort.
720
+ */
721
+ name: string;
722
+ /**
723
+ * The value corresponding to the descending sort.
724
+ */
725
+ desc: string;
726
+ /**
727
+ * The value corresponding to the ascending sort.
728
+ */
729
+ asc: string;
730
+ /**
731
+ * The value that will be applied on toggle.
732
+ */
733
+ next: string;
734
+ /**
735
+ * Whether this sort is hidden.
736
+ */
737
+ hidden: boolean;
738
+ }
739
+ interface Refinements {
740
+ /**
741
+ * The list of available filters.
742
+ */
743
+ filters: Array<FilterRefinement>;
744
+ /**
745
+ * The list of available sorts.
746
+ */
747
+ sorts: Array<SortRefinement>;
748
+ /**
749
+ * The URL scope for these refinements.
750
+ */
751
+ scope?: string;
752
+ /**
753
+ * The scope keys for these refinements.
754
+ */
755
+ keys: {
756
+ /**
757
+ * The scope key for sorting.
758
+ */
759
+ sorts: string;
760
+ /**
761
+ * The scope key for filtering.
762
+ */
763
+ filters: string;
764
+ };
765
+ }
766
+ }
767
+ declare function useRefinements<Properties extends object, RefinementsKey extends {
768
+ [K in keyof Properties]: Properties[K] extends Refinements ? K : never;
769
+ }[keyof Properties]>(properties: Properties, refinementsKeys: RefinementsKey, defaultOptions?: AvailableHybridRequestOptions): {
770
+ /**
771
+ * Available filters.
772
+ */
773
+ filters: FilterRefinement[];
774
+ /**
775
+ * Available sorts.
776
+ */
777
+ sorts: SortRefinement[];
778
+ /**
779
+ * Resets all filters and sorts.
780
+ */
781
+ reset: (options?: AvailableHybridRequestOptions) => Promise<_hybridly_core.NavigationResponse>;
782
+ /**
783
+ * Toggles the specified sort.
784
+ */
785
+ toggleSort: (sortName: string, options?: ToggleSortOptions) => Promise<_hybridly_core.NavigationResponse | undefined>;
786
+ /**
787
+ * Whether a sort is active.
788
+ */
789
+ isSorting: () => boolean;
790
+ /**
791
+ * Whether a filter is active.
792
+ */
793
+ isFiltering: () => boolean;
794
+ /**
795
+ * The current sorts.
796
+ */
797
+ currentSorts: () => Array<SortRefinement>;
798
+ /**
799
+ * The current filters.
800
+ */
801
+ currentFilters: () => Array<FilterRefinement>;
802
+ /**
803
+ * Clears the given filter.
804
+ */
805
+ clearFilter: (filter: string, options?: AvailableHybridRequestOptions) => Promise<_hybridly_core.NavigationResponse>;
806
+ /**
807
+ * Resets all sorts.
808
+ */
809
+ clearSorts: (options?: AvailableHybridRequestOptions) => Promise<_hybridly_core.NavigationResponse>;
810
+ /**
811
+ * Resets all filters.
812
+ */
813
+ clearFilters: (options?: AvailableHybridRequestOptions) => Promise<_hybridly_core.NavigationResponse>;
814
+ /**
815
+ * Applies the given filter.
816
+ */
817
+ applyFilter: (filter: string, value: any, options?: AvailableHybridRequestOptions) => Promise<_hybridly_core.NavigationResponse | undefined>;
818
+ };
819
+
820
+ export { Layout, RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, registerHook, resolvePageComponent, 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, showDomainsDisabledErrorModal, 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';
@@ -637,15 +637,15 @@ function useForm(options) {
637
637
  if (keys.length === 0) {
638
638
  return isDirty.value;
639
639
  }
640
- return keys.some((key) => !isEqual(toRaw(fields[key]), toRaw(initial[key])));
640
+ return keys.some((key) => !isEqual(toRaw(getByPath(fields, key)), toRaw(getByPath(initial, key))));
641
641
  }
642
642
  function clearError(key) {
643
- delete errors.value[key];
643
+ unsetPropertyAtPath(errors.value, key);
644
644
  }
645
645
  function setErrors(incoming) {
646
646
  clearErrors();
647
- Object.entries(incoming).forEach(([key, value]) => {
648
- errors.value[key] = value;
647
+ Object.entries(incoming).forEach(([path, value]) => {
648
+ setValueAtPath(errors.value, path, value);
649
649
  });
650
650
  }
651
651
  function abort() {
@@ -769,4 +769,104 @@ function useDialog() {
769
769
  };
770
770
  }
771
771
 
772
- export { RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, registerHook, resolvePageComponent, useBackForward, useContext, useDialog, useForm, useHistoryState, usePaginator, useProperties, useProperty };
772
+ function useRefinements(properties, refinementsKeys, defaultOptions = {}) {
773
+ const refinements = computed(() => properties[refinementsKeys]);
774
+ const sortsKey = computed(() => refinements.value.keys.sorts);
775
+ const filtersKey = computed(() => refinements.value.keys.filters);
776
+ async function reset(options = {}) {
777
+ return await router.reload({
778
+ ...defaultOptions,
779
+ ...options,
780
+ data: {
781
+ [filtersKey.value]: void 0,
782
+ [sortsKey.value]: void 0
783
+ }
784
+ });
785
+ }
786
+ async function clearFilters(options = {}) {
787
+ return await router.reload({
788
+ ...defaultOptions,
789
+ ...options,
790
+ data: {
791
+ [filtersKey.value]: void 0
792
+ }
793
+ });
794
+ }
795
+ async function clearFilter(filter, options = {}) {
796
+ return await router.reload({
797
+ ...defaultOptions,
798
+ ...options,
799
+ data: {
800
+ [filtersKey.value]: {
801
+ [filter]: void 0
802
+ }
803
+ }
804
+ });
805
+ }
806
+ async function applyFilter(filter, value, options = {}) {
807
+ if (!refinements.value.filters.find(({ name }) => name === filter)) {
808
+ return;
809
+ }
810
+ return await router.reload({
811
+ ...defaultOptions,
812
+ ...options,
813
+ data: {
814
+ [filtersKey.value]: {
815
+ [filter]: value === "" ? void 0 : value
816
+ }
817
+ }
818
+ });
819
+ }
820
+ async function clearSorts(options = {}) {
821
+ return await router.reload({
822
+ ...defaultOptions,
823
+ ...options,
824
+ data: {
825
+ [sortsKey.value]: void 0
826
+ }
827
+ });
828
+ }
829
+ function currentSorts() {
830
+ return refinements.value.sorts.filter(({ is_active }) => is_active);
831
+ }
832
+ function currentFilters() {
833
+ return refinements.value.filters.filter(({ is_active }) => is_active);
834
+ }
835
+ function isSorting() {
836
+ return currentSorts().length !== 0;
837
+ }
838
+ function isFiltering() {
839
+ return currentFilters().length !== 0;
840
+ }
841
+ async function toggleSort(sortName, options) {
842
+ const sort = refinements.value.sorts.find(({ name }) => name === sortName);
843
+ if (!sort) {
844
+ console.warn(`[Refinement] Sort "${sortName} does not exist."`);
845
+ return;
846
+ }
847
+ const next = options?.direction ? sort[options?.direction] : sort.next;
848
+ return await router.reload({
849
+ ...defaultOptions,
850
+ ...options,
851
+ data: {
852
+ [sortsKey.value]: next || void 0
853
+ }
854
+ });
855
+ }
856
+ return {
857
+ filters: toReactive(refinements.value.filters),
858
+ sorts: toReactive(refinements.value.sorts),
859
+ reset,
860
+ toggleSort,
861
+ isSorting,
862
+ isFiltering,
863
+ currentSorts,
864
+ currentFilters,
865
+ clearFilter,
866
+ clearSorts,
867
+ clearFilters,
868
+ applyFilter
869
+ };
870
+ }
871
+
872
+ export { RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, registerHook, resolvePageComponent, 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.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Vue adapter for Hybridly",
5
5
  "keywords": [
6
6
  "hybridly",
@@ -44,13 +44,13 @@
44
44
  "lodash.isequal": "^4.5.0",
45
45
  "nprogress": "^0.2.0",
46
46
  "qs": "^6.11.1",
47
- "@hybridly/config": "0.2.1",
48
- "@hybridly/core": "0.2.1",
49
- "@hybridly/progress-plugin": "0.2.1",
50
- "@hybridly/utils": "0.2.1"
47
+ "@hybridly/config": "0.3.1",
48
+ "@hybridly/core": "0.3.1",
49
+ "@hybridly/progress-plugin": "0.3.1",
50
+ "@hybridly/utils": "0.3.1"
51
51
  },
52
52
  "devDependencies": {
53
- "@types/lodash": "^4.14.192",
53
+ "@types/lodash": "^4.14.194",
54
54
  "@types/lodash.clonedeep": "^4.5.7",
55
55
  "@types/lodash.isequal": "^4.5.6",
56
56
  "@types/nprogress": "^0.2.0",