@hybridly/vue 0.0.1-alpha.13 → 0.0.1-alpha.15
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 +53 -182
- package/dist/index.d.ts +30 -33
- package/dist/index.mjs +56 -185
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -23,13 +23,6 @@ const state = {
|
|
|
23
23
|
viewKey: vue.ref(),
|
|
24
24
|
dialog: vue.shallowRef(),
|
|
25
25
|
dialogKey: vue.ref(),
|
|
26
|
-
routes: vue.ref(),
|
|
27
|
-
setRoutes(routes) {
|
|
28
|
-
utils.debug.adapter("vue:state:routes", "Setting routes:", routes);
|
|
29
|
-
if (routes) {
|
|
30
|
-
state.routes.value = vue.unref(routes);
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
26
|
setView(view) {
|
|
34
27
|
utils.debug.adapter("vue:state:view", "Setting view:", view);
|
|
35
28
|
state.view.value = view;
|
|
@@ -63,13 +56,12 @@ const state = {
|
|
|
63
56
|
|
|
64
57
|
const wrapper = vue.defineComponent({
|
|
65
58
|
name: "Hybridly",
|
|
66
|
-
setup(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
59
|
+
setup() {
|
|
60
|
+
const properties = vue.ref();
|
|
61
|
+
vue.watch(() => state.context.value?.view?.properties, (value) => {
|
|
62
|
+
utils.debug.adapter("vue:properties", "Updating properties.", vue.toRaw(value));
|
|
63
|
+
properties.value = value;
|
|
64
|
+
}, { immediate: true });
|
|
73
65
|
function renderLayout(child) {
|
|
74
66
|
utils.debug.adapter("vue:render:layout", "Rendering layout.");
|
|
75
67
|
if (typeof state.view.value?.layout === "function") {
|
|
@@ -80,14 +72,14 @@ const wrapper = vue.defineComponent({
|
|
|
80
72
|
layout.inheritAttrs = !!layout.inheritAttrs;
|
|
81
73
|
return vue.h(layout, {
|
|
82
74
|
...state.view.value?.layoutProperties ?? {},
|
|
83
|
-
...
|
|
75
|
+
...properties.value
|
|
84
76
|
}, () => child2);
|
|
85
77
|
});
|
|
86
78
|
}
|
|
87
79
|
return [
|
|
88
80
|
vue.h(state.view.value?.layout, {
|
|
89
81
|
...state.view.value?.layoutProperties ?? {},
|
|
90
|
-
...
|
|
82
|
+
...properties.value
|
|
91
83
|
}, () => child),
|
|
92
84
|
renderDialog()
|
|
93
85
|
];
|
|
@@ -96,7 +88,7 @@ const wrapper = vue.defineComponent({
|
|
|
96
88
|
utils.debug.adapter("vue:render:view", "Rendering view.");
|
|
97
89
|
state.view.value.inheritAttrs = !!state.view.value.inheritAttrs;
|
|
98
90
|
return vue.h(state.view.value, {
|
|
99
|
-
...
|
|
91
|
+
...properties.value,
|
|
100
92
|
key: state.viewKey.value
|
|
101
93
|
});
|
|
102
94
|
}
|
|
@@ -109,29 +101,24 @@ const wrapper = vue.defineComponent({
|
|
|
109
101
|
});
|
|
110
102
|
}
|
|
111
103
|
}
|
|
112
|
-
return () => {
|
|
113
|
-
if (state.view.value) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
state.view.value.layoutProperties = state.viewLayoutProperties.value;
|
|
121
|
-
state.viewLayoutProperties.value = void 0;
|
|
122
|
-
}
|
|
123
|
-
if (state.view.value.layout) {
|
|
124
|
-
return renderLayout(view);
|
|
125
|
-
}
|
|
126
|
-
return [view, renderDialog()];
|
|
104
|
+
return (...a) => {
|
|
105
|
+
if (!state.view.value) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
utils.debug.adapter("vue:render:wrapper", "Rendering wrapper component.", a.map(vue.toRaw));
|
|
109
|
+
const view = renderView();
|
|
110
|
+
if (state.viewLayout.value) {
|
|
111
|
+
state.view.value.layout = state.viewLayout.value;
|
|
127
112
|
}
|
|
113
|
+
if (state.viewLayoutProperties.value) {
|
|
114
|
+
state.view.value.layoutProperties = state.viewLayoutProperties.value;
|
|
115
|
+
state.viewLayoutProperties.value = void 0;
|
|
116
|
+
}
|
|
117
|
+
if (state.view.value.layout) {
|
|
118
|
+
return renderLayout(view);
|
|
119
|
+
}
|
|
120
|
+
return [view, renderDialog()];
|
|
128
121
|
};
|
|
129
|
-
},
|
|
130
|
-
props: {
|
|
131
|
-
context: {
|
|
132
|
-
type: Object,
|
|
133
|
-
required: true
|
|
134
|
-
}
|
|
135
122
|
}
|
|
136
123
|
});
|
|
137
124
|
|
|
@@ -173,8 +160,8 @@ function setupDevtools(app) {
|
|
|
173
160
|
});
|
|
174
161
|
payload.instanceData.state.push({
|
|
175
162
|
type: hybridlyStateType,
|
|
176
|
-
key: "
|
|
177
|
-
value: state.
|
|
163
|
+
key: "routing",
|
|
164
|
+
value: state.context.value?.routing
|
|
178
165
|
});
|
|
179
166
|
});
|
|
180
167
|
api.on.editComponentState((payload) => {
|
|
@@ -231,7 +218,7 @@ function setupDevtools(app) {
|
|
|
231
218
|
});
|
|
232
219
|
});
|
|
233
220
|
}
|
|
234
|
-
const
|
|
221
|
+
const devtools = {
|
|
235
222
|
install(app) {
|
|
236
223
|
if (process.env.NODE_ENV === "development" || __VUE_PROD_DEVTOOLS__) {
|
|
237
224
|
setupDevtools(app);
|
|
@@ -267,18 +254,26 @@ async function initializeHybridly(options) {
|
|
|
267
254
|
},
|
|
268
255
|
payload
|
|
269
256
|
}));
|
|
270
|
-
|
|
257
|
+
if (typeof window !== "undefined") {
|
|
258
|
+
window.addEventListener("hybridly:routing", (event) => {
|
|
259
|
+
state.context.value?.adapter.updateRoutingConfiguration(event.detail);
|
|
260
|
+
});
|
|
261
|
+
window.dispatchEvent(new CustomEvent("hybridly:routing", { detail: window?.hybridly?.routing }));
|
|
262
|
+
}
|
|
263
|
+
const render = () => vue.h(wrapper);
|
|
271
264
|
if (options.setup) {
|
|
272
265
|
return await options.setup({
|
|
273
266
|
element,
|
|
274
267
|
wrapper,
|
|
275
268
|
render,
|
|
276
|
-
hybridly:
|
|
269
|
+
hybridly: devtools,
|
|
277
270
|
props: { context: state.context.value }
|
|
278
271
|
});
|
|
279
272
|
}
|
|
280
273
|
const app = vue.createApp({ render });
|
|
281
|
-
|
|
274
|
+
if (options.devtools !== false) {
|
|
275
|
+
app.use(devtools);
|
|
276
|
+
}
|
|
282
277
|
await options.enhanceVue?.(app);
|
|
283
278
|
return app.mount(element);
|
|
284
279
|
}
|
|
@@ -304,14 +299,6 @@ function prepare(options) {
|
|
|
304
299
|
}
|
|
305
300
|
throw new Error("Either `initializeHybridly#resolve` or `initializeHybridly#pages` should be defined.");
|
|
306
301
|
};
|
|
307
|
-
if (typeof window !== "undefined") {
|
|
308
|
-
state.setRoutes(window?.hybridly?.routes);
|
|
309
|
-
window.addEventListener("hybridly:routes", (event) => {
|
|
310
|
-
if (event.detail) {
|
|
311
|
-
state.setRoutes(event.detail);
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
302
|
if (options.progress !== false) {
|
|
316
303
|
options.plugins = [
|
|
317
304
|
progressPlugin.progress(typeof options.progress === "object" ? options.progress : {}),
|
|
@@ -439,12 +426,12 @@ const HybridlyImports = {
|
|
|
439
426
|
"useHistoryState",
|
|
440
427
|
"usePaginator",
|
|
441
428
|
"defineLayout",
|
|
442
|
-
"defineLayoutProperties"
|
|
443
|
-
"route"
|
|
429
|
+
"defineLayoutProperties"
|
|
444
430
|
],
|
|
445
431
|
"hybridly": [
|
|
446
432
|
"registerHook",
|
|
447
433
|
"router",
|
|
434
|
+
"route",
|
|
448
435
|
"can"
|
|
449
436
|
]
|
|
450
437
|
};
|
|
@@ -521,7 +508,7 @@ function safeClone(obj) {
|
|
|
521
508
|
return utils.clone(vue.toRaw(obj));
|
|
522
509
|
}
|
|
523
510
|
function useForm(options) {
|
|
524
|
-
const shouldRemember = options?.key
|
|
511
|
+
const shouldRemember = !!options?.key;
|
|
525
512
|
const historyKey = options?.key ?? "form:default";
|
|
526
513
|
const historyData = shouldRemember ? core.router.history.get(historyKey) : void 0;
|
|
527
514
|
const timeoutIds = {
|
|
@@ -551,13 +538,14 @@ function useForm(options) {
|
|
|
551
538
|
function submit(optionsOverrides) {
|
|
552
539
|
const url = typeof options.url === "function" ? options.url() : options.url;
|
|
553
540
|
const data = typeof options.transform === "function" ? options.transform?.(fields) : fields;
|
|
541
|
+
const preserveState = optionsOverrides?.preserveState ?? options.preserveState;
|
|
554
542
|
return core.router.navigate({
|
|
555
543
|
...options,
|
|
556
544
|
url: url ?? state.context.value?.url,
|
|
557
545
|
method: options.method ?? "POST",
|
|
558
546
|
...optionsOverrides,
|
|
559
547
|
data: safeClone(data),
|
|
560
|
-
preserveState:
|
|
548
|
+
preserveState: preserveState === void 0 && options.method !== "GET" ? true : preserveState,
|
|
561
549
|
hooks: {
|
|
562
550
|
before: (navigation, context) => {
|
|
563
551
|
failed.value = false;
|
|
@@ -565,7 +553,6 @@ function useForm(options) {
|
|
|
565
553
|
recentlySuccessful.value = false;
|
|
566
554
|
clearTimeout(timeoutIds.recentlySuccessful);
|
|
567
555
|
clearTimeout(timeoutIds.recentlyFailed);
|
|
568
|
-
clearErrors();
|
|
569
556
|
return options.hooks?.before?.(navigation, context);
|
|
570
557
|
},
|
|
571
558
|
start: (context) => {
|
|
@@ -584,6 +571,7 @@ function useForm(options) {
|
|
|
584
571
|
return options.hooks?.error?.(incoming, context);
|
|
585
572
|
},
|
|
586
573
|
success: (payload, context) => {
|
|
574
|
+
clearErrors();
|
|
587
575
|
if (options?.reset !== false) {
|
|
588
576
|
reset();
|
|
589
577
|
}
|
|
@@ -608,6 +596,12 @@ function useForm(options) {
|
|
|
608
596
|
clearError(key);
|
|
609
597
|
});
|
|
610
598
|
}
|
|
599
|
+
function hasDirty(...keys) {
|
|
600
|
+
if (keys.length === 0) {
|
|
601
|
+
return isDirty.value;
|
|
602
|
+
}
|
|
603
|
+
return keys.some((key) => !isEqual__default(vue.toRaw(fields[key]), vue.toRaw(initial[key])));
|
|
604
|
+
}
|
|
611
605
|
function clearError(key) {
|
|
612
606
|
errors.value[key] = void 0;
|
|
613
607
|
}
|
|
@@ -633,6 +627,7 @@ function useForm(options) {
|
|
|
633
627
|
setErrors,
|
|
634
628
|
clearErrors,
|
|
635
629
|
clearError,
|
|
630
|
+
hasDirty,
|
|
636
631
|
submitWithOptions: submit,
|
|
637
632
|
submit: () => submit(),
|
|
638
633
|
hasErrors: vue.computed(() => Object.values(errors.value).length > 0),
|
|
@@ -714,131 +709,8 @@ function defineLayoutProperties(properties) {
|
|
|
714
709
|
state.setViewLayoutProperties(properties);
|
|
715
710
|
}
|
|
716
711
|
|
|
717
|
-
class Route {
|
|
718
|
-
constructor(name, absolute) {
|
|
719
|
-
this.name = name;
|
|
720
|
-
this.absolute = absolute;
|
|
721
|
-
this.definition = Route.getDefinition(name);
|
|
722
|
-
}
|
|
723
|
-
static getDefinition(name) {
|
|
724
|
-
if (!state.routes.value) {
|
|
725
|
-
throw new Error("Routing is not initialized. Make sure the Vite plugin is enabled and that `virtual:hybridly/router` is imported.");
|
|
726
|
-
}
|
|
727
|
-
const routes = state.routes.value;
|
|
728
|
-
const route = routes?.routes?.[name];
|
|
729
|
-
if (!route) {
|
|
730
|
-
throw new Error(`Route ${name.toString()} does not exist.`);
|
|
731
|
-
}
|
|
732
|
-
return route;
|
|
733
|
-
}
|
|
734
|
-
get template() {
|
|
735
|
-
const origin = !this.absolute ? "" : this.definition.domain ? `${state.routes.value?.url.match(/^\w+:\/\//)?.[0]}${this.definition.domain}${state.routes.value?.port ? `:${state.routes.value?.port}` : ""}` : state.routes.value?.url;
|
|
736
|
-
return `${origin}/${this.definition.uri}`.replace(/\/+$/, "");
|
|
737
|
-
}
|
|
738
|
-
get parameterSegments() {
|
|
739
|
-
return this.template.match(/{[^}?]+\??}/g)?.map((segment) => ({
|
|
740
|
-
name: segment.replace(/{|\??}/g, ""),
|
|
741
|
-
required: !/\?}$/.test(segment)
|
|
742
|
-
})) ?? [];
|
|
743
|
-
}
|
|
744
|
-
matchesUrl(url) {
|
|
745
|
-
if (!this.definition.methods.includes("GET")) {
|
|
746
|
-
return false;
|
|
747
|
-
}
|
|
748
|
-
const pattern = this.template.replace(/(\/?){([^}?]*)(\??)}/g, (_, slash, segment, optional) => {
|
|
749
|
-
const regex = `(?<${segment}>${this.definition.wheres?.[segment]?.replace(/(^\^)|(\$$)/g, "") || "[^/?]+"})`;
|
|
750
|
-
return optional ? `(${slash}${regex})?` : `${slash}${regex}`;
|
|
751
|
-
}).replace(/^\w+:\/\//, "");
|
|
752
|
-
const [location, query] = url.replace(/^\w+:\/\//, "").split("?");
|
|
753
|
-
const matches = new RegExp(`^${pattern}/?$`).exec(location);
|
|
754
|
-
return matches ? { params: matches.groups, query: qs.parse(query) } : false;
|
|
755
|
-
}
|
|
756
|
-
compile(params) {
|
|
757
|
-
const segments = this.parameterSegments;
|
|
758
|
-
if (!segments.length) {
|
|
759
|
-
return this.template;
|
|
760
|
-
}
|
|
761
|
-
return this.template.replace(/{([^}?]+)(\??)}/g, (_, segment, optional) => {
|
|
762
|
-
if (!optional && [null, void 0].includes(params?.[segment])) {
|
|
763
|
-
throw new Error(`Router error: [${segment}] parameter is required for route [${this.name}].`);
|
|
764
|
-
}
|
|
765
|
-
if (segments[segments.length - 1].name === segment && this.definition?.wheres?.[segment] === ".*") {
|
|
766
|
-
return encodeURIComponent(params[segment] ?? "").replace(/%2F/g, "/");
|
|
767
|
-
}
|
|
768
|
-
if (this.definition?.wheres?.[segment] && !new RegExp(`^${optional ? `(${this.definition?.wheres?.[segment]})?` : this.definition?.wheres?.[segment]}$`).test(params[segment] ?? "")) {
|
|
769
|
-
throw new Error(`Router error: [${segment}] parameter does not match required format [${this.definition?.wheres?.[segment]}] for route [${this.name}].`);
|
|
770
|
-
}
|
|
771
|
-
return encodeURIComponent(params[segment] ?? "");
|
|
772
|
-
}).replace(/\/+$/, "");
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
class Router extends String {
|
|
777
|
-
constructor(name, parameters, absolute = true) {
|
|
778
|
-
super();
|
|
779
|
-
this.route = new Route(name, absolute);
|
|
780
|
-
this.setParameters(parameters);
|
|
781
|
-
}
|
|
782
|
-
toString() {
|
|
783
|
-
const unhandled = Object.keys(this.parameters).filter((key) => !this.route.parameterSegments.some(({ name }) => name === key)).filter((key) => key !== "_query").reduce((result, current) => ({ ...result, [current]: this.parameters[current] }), {});
|
|
784
|
-
return this.route.compile(this.parameters) + qs.stringify({ ...unhandled, ...this.parameters._query }, {
|
|
785
|
-
addQueryPrefix: true,
|
|
786
|
-
arrayFormat: "indices",
|
|
787
|
-
encodeValuesOnly: true,
|
|
788
|
-
skipNulls: true,
|
|
789
|
-
encoder: (value, encoder) => typeof value === "boolean" ? Number(value).toString() : encoder(value)
|
|
790
|
-
});
|
|
791
|
-
}
|
|
792
|
-
static has(name) {
|
|
793
|
-
try {
|
|
794
|
-
Route.getDefinition(name);
|
|
795
|
-
return true;
|
|
796
|
-
} catch {
|
|
797
|
-
return false;
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
setParameters(parameters) {
|
|
801
|
-
this.parameters = parameters ?? {};
|
|
802
|
-
this.parameters = ["string", "number"].includes(typeof this.parameters) ? [this.parameters] : this.parameters;
|
|
803
|
-
const segments = this.route.parameterSegments.filter(({ name }) => !state.routes.value?.defaults[name]);
|
|
804
|
-
if (Array.isArray(this.parameters)) {
|
|
805
|
-
this.parameters = this.parameters.reduce((result, current, i) => segments[i] ? { ...result, [segments[i].name]: current } : typeof current === "object" ? { ...result, ...current } : { ...result, [current]: "" }, {});
|
|
806
|
-
} else if (segments.length === 1 && !this.parameters[segments[0].name] && (Reflect.has(this.parameters, Object.values(this.route.definition.bindings)[0]) || Reflect.has(this.parameters, "id"))) {
|
|
807
|
-
this.parameters = { [segments[0].name]: this.parameters };
|
|
808
|
-
}
|
|
809
|
-
this.parameters = {
|
|
810
|
-
...this.getDefaults(),
|
|
811
|
-
...this.substituteBindings()
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
getDefaults() {
|
|
815
|
-
return this.route.parameterSegments.filter(({ name }) => state.routes.value?.defaults[name]).reduce((result, { name }) => ({ ...result, [name]: state.routes.value?.defaults[name] }), {});
|
|
816
|
-
}
|
|
817
|
-
substituteBindings() {
|
|
818
|
-
return Object.entries(this.parameters).reduce((result, [key, value]) => {
|
|
819
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || !this.route.parameterSegments.some(({ name }) => name === key)) {
|
|
820
|
-
return { ...result, [key]: value };
|
|
821
|
-
}
|
|
822
|
-
if (!Reflect.has(value, this.route.definition.bindings[key])) {
|
|
823
|
-
if (Reflect.has(value, "id")) {
|
|
824
|
-
this.route.definition.bindings[key] = "id";
|
|
825
|
-
} else {
|
|
826
|
-
throw new Error(`Router error: object passed as [${key}] parameter is missing route model binding key [${this.route.definition.bindings?.[key]}].`);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
return { ...result, [key]: value[this.route.definition.bindings[key]] };
|
|
830
|
-
}, {});
|
|
831
|
-
}
|
|
832
|
-
valueOf() {
|
|
833
|
-
return this.toString();
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
function route(name, parameters, absolute) {
|
|
838
|
-
return new Router(name, parameters, absolute).toString();
|
|
839
|
-
}
|
|
840
|
-
|
|
841
712
|
exports.can = core.can;
|
|
713
|
+
exports.route = core.route;
|
|
842
714
|
exports.router = core.router;
|
|
843
715
|
exports.HybridlyImports = HybridlyImports;
|
|
844
716
|
exports.HybridlyResolver = HybridlyResolver;
|
|
@@ -847,7 +719,6 @@ exports.defineLayout = defineLayout;
|
|
|
847
719
|
exports.defineLayoutProperties = defineLayoutProperties;
|
|
848
720
|
exports.initializeHybridly = initializeHybridly;
|
|
849
721
|
exports.resolvePageComponent = resolvePageComponent;
|
|
850
|
-
exports.route = route;
|
|
851
722
|
exports.useBackForward = useBackForward;
|
|
852
723
|
exports.useContext = useContext;
|
|
853
724
|
exports.useForm = useForm;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ 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
4
|
import { HybridPayload as HybridPayload$1, ResolveComponent as ResolveComponent$1, RouterContextOptions, Plugin as Plugin$1, RouterContext, Method as Method$1, HybridRequestOptions as HybridRequestOptions$1, UrlResolvable as UrlResolvable$1 } from '@hybridly/core';
|
|
5
|
-
export { can, router } from '@hybridly/core';
|
|
5
|
+
export { can, route, router } from '@hybridly/core';
|
|
6
6
|
import { ProgressOptions } from '@hybridly/progress-plugin';
|
|
7
7
|
import { Axios, AxiosResponse, AxiosProgressEvent } from 'axios';
|
|
8
8
|
import * as _vue_shared from '@vue/shared';
|
|
@@ -28,6 +28,8 @@ interface HybridlyOptions {
|
|
|
28
28
|
serializer?: RouterContextOptions['serializer'];
|
|
29
29
|
/** Clean up the host element's payload dataset after loading. */
|
|
30
30
|
cleanup?: boolean;
|
|
31
|
+
/** Whether to set up the devtools plugin. */
|
|
32
|
+
devtools?: boolean;
|
|
31
33
|
/** Progressbar options. */
|
|
32
34
|
progress?: boolean | Partial<ProgressOptions>;
|
|
33
35
|
/** Sets up the hybridly router. */
|
|
@@ -65,7 +67,7 @@ declare const RouterLink: vue.DefineComponent<{
|
|
|
65
67
|
default: string;
|
|
66
68
|
};
|
|
67
69
|
method: {
|
|
68
|
-
type: PropType<
|
|
70
|
+
type: PropType<Method$1 | "get" | "post" | "put" | "patch" | "delete">;
|
|
69
71
|
default: string;
|
|
70
72
|
};
|
|
71
73
|
data: {
|
|
@@ -95,7 +97,7 @@ declare const RouterLink: vue.DefineComponent<{
|
|
|
95
97
|
default: string;
|
|
96
98
|
};
|
|
97
99
|
method: {
|
|
98
|
-
type: PropType<
|
|
100
|
+
type: PropType<Method$1 | "get" | "post" | "put" | "patch" | "delete">;
|
|
99
101
|
default: string;
|
|
100
102
|
};
|
|
101
103
|
data: {
|
|
@@ -127,7 +129,7 @@ declare const RouterLink: vue.DefineComponent<{
|
|
|
127
129
|
default: string;
|
|
128
130
|
};
|
|
129
131
|
method: {
|
|
130
|
-
type: PropType<
|
|
132
|
+
type: PropType<Method$1 | "get" | "post" | "put" | "patch" | "delete">;
|
|
131
133
|
default: string;
|
|
132
134
|
};
|
|
133
135
|
data: {
|
|
@@ -149,7 +151,7 @@ declare const RouterLink: vue.DefineComponent<{
|
|
|
149
151
|
}>>, {
|
|
150
152
|
data: RequestData;
|
|
151
153
|
href: string;
|
|
152
|
-
method:
|
|
154
|
+
method: Method$1 | "get" | "post" | "put" | "patch" | "delete";
|
|
153
155
|
options: Omit<HybridRequestOptions$1, "data" | "url" | "method">;
|
|
154
156
|
as: string | Record<string, any>;
|
|
155
157
|
external: boolean;
|
|
@@ -254,6 +256,20 @@ interface Hooks {
|
|
|
254
256
|
navigated: (options: NavigationOptions, context: InternalRouterContext) => MaybePromise<void>;
|
|
255
257
|
}
|
|
256
258
|
|
|
259
|
+
interface RoutingConfiguration {
|
|
260
|
+
url: string;
|
|
261
|
+
port?: number;
|
|
262
|
+
defaults: Record<string, any>;
|
|
263
|
+
routes: Record<string, RouteDefinition>;
|
|
264
|
+
}
|
|
265
|
+
interface RouteDefinition {
|
|
266
|
+
uri: string;
|
|
267
|
+
method: Method[];
|
|
268
|
+
bindings: Record<string, string>;
|
|
269
|
+
domain?: string;
|
|
270
|
+
wheres?: Record<string, string>;
|
|
271
|
+
}
|
|
272
|
+
|
|
257
273
|
type UrlResolvable = string | URL | Location;
|
|
258
274
|
type UrlTransformable = Partial<Omit<URL, 'searchParams' | 'toJSON' | 'toString'>> & {
|
|
259
275
|
query?: any;
|
|
@@ -300,7 +316,7 @@ interface HybridRequestOptions extends Omit<NavigationOptions, 'payload'> {
|
|
|
300
316
|
/** The URL to navigation. */
|
|
301
317
|
url?: UrlResolvable;
|
|
302
318
|
/** HTTP verb to use for the request. */
|
|
303
|
-
method?: Method
|
|
319
|
+
method?: Method | Lowercase<Method>;
|
|
304
320
|
/** Body of the request. */
|
|
305
321
|
data?: RequestData;
|
|
306
322
|
/** Which properties to update for this navigation. Other properties will be ignored. */
|
|
@@ -395,7 +411,7 @@ interface InternalRouterContext {
|
|
|
395
411
|
/** The current local asset version. */
|
|
396
412
|
version: string;
|
|
397
413
|
/** The current adapter's functions. */
|
|
398
|
-
adapter:
|
|
414
|
+
adapter: ResolvedAdapter;
|
|
399
415
|
/** Scroll positions of the current page's DOM elements. */
|
|
400
416
|
scrollRegions: ScrollRegion[];
|
|
401
417
|
/** Arbitrary state. */
|
|
@@ -410,6 +426,8 @@ interface InternalRouterContext {
|
|
|
410
426
|
hooks: Partial<Record<keyof Hooks, Array<Function>>>;
|
|
411
427
|
/** The Axios instance. */
|
|
412
428
|
axios: Axios;
|
|
429
|
+
/** Routing configuration. */
|
|
430
|
+
routing?: RoutingConfiguration;
|
|
413
431
|
}
|
|
414
432
|
/** Adapter-specific functions. */
|
|
415
433
|
interface Adapter {
|
|
@@ -422,6 +440,9 @@ interface Adapter {
|
|
|
422
440
|
/** Called when the context is updated. */
|
|
423
441
|
update?: (context: InternalRouterContext) => void;
|
|
424
442
|
}
|
|
443
|
+
interface ResolvedAdapter extends Adapter {
|
|
444
|
+
updateRoutingConfiguration: (routing?: RoutingConfiguration) => void;
|
|
445
|
+
}
|
|
425
446
|
interface ScrollRegion {
|
|
426
447
|
top: number;
|
|
427
448
|
left: number;
|
|
@@ -451,6 +472,7 @@ declare function useForm<T extends Fields = Fields>(options: FormOptions<T>): {
|
|
|
451
472
|
setErrors: (incoming: Record<string, string>) => void;
|
|
452
473
|
clearErrors: (...keys: (keyof T)[]) => void;
|
|
453
474
|
clearError: (key: keyof T) => void;
|
|
475
|
+
hasDirty: (...keys: (keyof T)[]) => boolean;
|
|
454
476
|
submitWithOptions: (optionsOverrides?: Omit<HybridRequestOptions$1, 'data'>) => Promise<_hybridly_core.NavigationResponse>;
|
|
455
477
|
submit: () => Promise<_hybridly_core.NavigationResponse>;
|
|
456
478
|
hasErrors: boolean;
|
|
@@ -616,29 +638,4 @@ declare function defineLayout(layouts: Layout[]): void;
|
|
|
616
638
|
*/
|
|
617
639
|
declare function defineLayoutProperties<T extends Record<string, K>, K = any>(properties: T): void;
|
|
618
640
|
|
|
619
|
-
|
|
620
|
-
url: string;
|
|
621
|
-
port?: number;
|
|
622
|
-
defaults: Record<string, any>;
|
|
623
|
-
}
|
|
624
|
-
interface RouteDefinition {
|
|
625
|
-
uri: string;
|
|
626
|
-
methods: Method$1[];
|
|
627
|
-
bindings: Record<string, string>;
|
|
628
|
-
domain?: string;
|
|
629
|
-
wheres?: Record<string, string>;
|
|
630
|
-
}
|
|
631
|
-
interface RouteCollection extends RouterConfiguration {
|
|
632
|
-
routes: Record<string, RouteDefinition>;
|
|
633
|
-
}
|
|
634
|
-
interface GlobalRouteCollection extends RouteCollection {
|
|
635
|
-
}
|
|
636
|
-
type RouteName = keyof GlobalRouteCollection['routes'];
|
|
637
|
-
type RouteParameters<T extends RouteName> = Record<keyof GlobalRouteCollection['routes'][T]['bindings'], any> & Record<string, any>;
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* Generates a route from the given route name.
|
|
641
|
-
*/
|
|
642
|
-
declare function route<T extends RouteName>(name: T, parameters?: RouteParameters<T>, absolute?: boolean): string;
|
|
643
|
-
|
|
644
|
-
export { AutoImportResolverOptions, GlobalRouteCollection, HybridlyImports, HybridlyResolver, Layout, RouteCollection, RouteDefinition, RouteName, RouteParameters, RouterConfiguration, RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, resolvePageComponent, route, useBackForward, useContext, useForm, useHistoryState, usePaginator, useProperties, useProperty, useTypedProperty };
|
|
641
|
+
export { AutoImportResolverOptions, HybridlyImports, HybridlyResolver, Layout, RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, resolvePageComponent, useBackForward, useContext, useForm, useHistoryState, usePaginator, useProperties, useProperty, useTypedProperty };
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ref, shallowRef, unref, triggerRef, defineComponent, h, createApp, isRef, reactive, readonly, computed
|
|
1
|
+
import { ref, shallowRef, unref, triggerRef, defineComponent, watch, toRaw, h, createApp, isRef, reactive, readonly, computed } from 'vue';
|
|
2
2
|
import { registerHook, createRouter, makeUrl, router } from '@hybridly/core';
|
|
3
|
-
export { can, router } from '@hybridly/core';
|
|
3
|
+
export { can, route, router } from '@hybridly/core';
|
|
4
4
|
import { debug, showPageComponentErrorModal, merge, clone } from '@hybridly/utils';
|
|
5
5
|
import { progress } from '@hybridly/progress-plugin';
|
|
6
6
|
import { setupDevtoolsPlugin } from '@vue/devtools-api';
|
|
7
|
-
import qs
|
|
7
|
+
import qs from 'qs';
|
|
8
8
|
import isEqual from 'lodash.isequal';
|
|
9
9
|
|
|
10
10
|
const state = {
|
|
@@ -15,13 +15,6 @@ const state = {
|
|
|
15
15
|
viewKey: ref(),
|
|
16
16
|
dialog: shallowRef(),
|
|
17
17
|
dialogKey: ref(),
|
|
18
|
-
routes: ref(),
|
|
19
|
-
setRoutes(routes) {
|
|
20
|
-
debug.adapter("vue:state:routes", "Setting routes:", routes);
|
|
21
|
-
if (routes) {
|
|
22
|
-
state.routes.value = unref(routes);
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
18
|
setView(view) {
|
|
26
19
|
debug.adapter("vue:state:view", "Setting view:", view);
|
|
27
20
|
state.view.value = view;
|
|
@@ -55,13 +48,12 @@ const state = {
|
|
|
55
48
|
|
|
56
49
|
const wrapper = defineComponent({
|
|
57
50
|
name: "Hybridly",
|
|
58
|
-
setup(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
51
|
+
setup() {
|
|
52
|
+
const properties = ref();
|
|
53
|
+
watch(() => state.context.value?.view?.properties, (value) => {
|
|
54
|
+
debug.adapter("vue:properties", "Updating properties.", toRaw(value));
|
|
55
|
+
properties.value = value;
|
|
56
|
+
}, { immediate: true });
|
|
65
57
|
function renderLayout(child) {
|
|
66
58
|
debug.adapter("vue:render:layout", "Rendering layout.");
|
|
67
59
|
if (typeof state.view.value?.layout === "function") {
|
|
@@ -72,14 +64,14 @@ const wrapper = defineComponent({
|
|
|
72
64
|
layout.inheritAttrs = !!layout.inheritAttrs;
|
|
73
65
|
return h(layout, {
|
|
74
66
|
...state.view.value?.layoutProperties ?? {},
|
|
75
|
-
...
|
|
67
|
+
...properties.value
|
|
76
68
|
}, () => child2);
|
|
77
69
|
});
|
|
78
70
|
}
|
|
79
71
|
return [
|
|
80
72
|
h(state.view.value?.layout, {
|
|
81
73
|
...state.view.value?.layoutProperties ?? {},
|
|
82
|
-
...
|
|
74
|
+
...properties.value
|
|
83
75
|
}, () => child),
|
|
84
76
|
renderDialog()
|
|
85
77
|
];
|
|
@@ -88,7 +80,7 @@ const wrapper = defineComponent({
|
|
|
88
80
|
debug.adapter("vue:render:view", "Rendering view.");
|
|
89
81
|
state.view.value.inheritAttrs = !!state.view.value.inheritAttrs;
|
|
90
82
|
return h(state.view.value, {
|
|
91
|
-
...
|
|
83
|
+
...properties.value,
|
|
92
84
|
key: state.viewKey.value
|
|
93
85
|
});
|
|
94
86
|
}
|
|
@@ -101,29 +93,24 @@ const wrapper = defineComponent({
|
|
|
101
93
|
});
|
|
102
94
|
}
|
|
103
95
|
}
|
|
104
|
-
return () => {
|
|
105
|
-
if (state.view.value) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
state.view.value.layoutProperties = state.viewLayoutProperties.value;
|
|
113
|
-
state.viewLayoutProperties.value = void 0;
|
|
114
|
-
}
|
|
115
|
-
if (state.view.value.layout) {
|
|
116
|
-
return renderLayout(view);
|
|
117
|
-
}
|
|
118
|
-
return [view, renderDialog()];
|
|
96
|
+
return (...a) => {
|
|
97
|
+
if (!state.view.value) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
debug.adapter("vue:render:wrapper", "Rendering wrapper component.", a.map(toRaw));
|
|
101
|
+
const view = renderView();
|
|
102
|
+
if (state.viewLayout.value) {
|
|
103
|
+
state.view.value.layout = state.viewLayout.value;
|
|
119
104
|
}
|
|
105
|
+
if (state.viewLayoutProperties.value) {
|
|
106
|
+
state.view.value.layoutProperties = state.viewLayoutProperties.value;
|
|
107
|
+
state.viewLayoutProperties.value = void 0;
|
|
108
|
+
}
|
|
109
|
+
if (state.view.value.layout) {
|
|
110
|
+
return renderLayout(view);
|
|
111
|
+
}
|
|
112
|
+
return [view, renderDialog()];
|
|
120
113
|
};
|
|
121
|
-
},
|
|
122
|
-
props: {
|
|
123
|
-
context: {
|
|
124
|
-
type: Object,
|
|
125
|
-
required: true
|
|
126
|
-
}
|
|
127
114
|
}
|
|
128
115
|
});
|
|
129
116
|
|
|
@@ -165,8 +152,8 @@ function setupDevtools(app) {
|
|
|
165
152
|
});
|
|
166
153
|
payload.instanceData.state.push({
|
|
167
154
|
type: hybridlyStateType,
|
|
168
|
-
key: "
|
|
169
|
-
value: state.
|
|
155
|
+
key: "routing",
|
|
156
|
+
value: state.context.value?.routing
|
|
170
157
|
});
|
|
171
158
|
});
|
|
172
159
|
api.on.editComponentState((payload) => {
|
|
@@ -223,7 +210,7 @@ function setupDevtools(app) {
|
|
|
223
210
|
});
|
|
224
211
|
});
|
|
225
212
|
}
|
|
226
|
-
const
|
|
213
|
+
const devtools = {
|
|
227
214
|
install(app) {
|
|
228
215
|
if (process.env.NODE_ENV === "development" || __VUE_PROD_DEVTOOLS__) {
|
|
229
216
|
setupDevtools(app);
|
|
@@ -259,18 +246,26 @@ async function initializeHybridly(options) {
|
|
|
259
246
|
},
|
|
260
247
|
payload
|
|
261
248
|
}));
|
|
262
|
-
|
|
249
|
+
if (typeof window !== "undefined") {
|
|
250
|
+
window.addEventListener("hybridly:routing", (event) => {
|
|
251
|
+
state.context.value?.adapter.updateRoutingConfiguration(event.detail);
|
|
252
|
+
});
|
|
253
|
+
window.dispatchEvent(new CustomEvent("hybridly:routing", { detail: window?.hybridly?.routing }));
|
|
254
|
+
}
|
|
255
|
+
const render = () => h(wrapper);
|
|
263
256
|
if (options.setup) {
|
|
264
257
|
return await options.setup({
|
|
265
258
|
element,
|
|
266
259
|
wrapper,
|
|
267
260
|
render,
|
|
268
|
-
hybridly:
|
|
261
|
+
hybridly: devtools,
|
|
269
262
|
props: { context: state.context.value }
|
|
270
263
|
});
|
|
271
264
|
}
|
|
272
265
|
const app = createApp({ render });
|
|
273
|
-
|
|
266
|
+
if (options.devtools !== false) {
|
|
267
|
+
app.use(devtools);
|
|
268
|
+
}
|
|
274
269
|
await options.enhanceVue?.(app);
|
|
275
270
|
return app.mount(element);
|
|
276
271
|
}
|
|
@@ -296,14 +291,6 @@ function prepare(options) {
|
|
|
296
291
|
}
|
|
297
292
|
throw new Error("Either `initializeHybridly#resolve` or `initializeHybridly#pages` should be defined.");
|
|
298
293
|
};
|
|
299
|
-
if (typeof window !== "undefined") {
|
|
300
|
-
state.setRoutes(window?.hybridly?.routes);
|
|
301
|
-
window.addEventListener("hybridly:routes", (event) => {
|
|
302
|
-
if (event.detail) {
|
|
303
|
-
state.setRoutes(event.detail);
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
294
|
if (options.progress !== false) {
|
|
308
295
|
options.plugins = [
|
|
309
296
|
progress(typeof options.progress === "object" ? options.progress : {}),
|
|
@@ -431,12 +418,12 @@ const HybridlyImports = {
|
|
|
431
418
|
"useHistoryState",
|
|
432
419
|
"usePaginator",
|
|
433
420
|
"defineLayout",
|
|
434
|
-
"defineLayoutProperties"
|
|
435
|
-
"route"
|
|
421
|
+
"defineLayoutProperties"
|
|
436
422
|
],
|
|
437
423
|
"hybridly": [
|
|
438
424
|
"registerHook",
|
|
439
425
|
"router",
|
|
426
|
+
"route",
|
|
440
427
|
"can"
|
|
441
428
|
]
|
|
442
429
|
};
|
|
@@ -513,7 +500,7 @@ function safeClone(obj) {
|
|
|
513
500
|
return clone(toRaw(obj));
|
|
514
501
|
}
|
|
515
502
|
function useForm(options) {
|
|
516
|
-
const shouldRemember = options?.key
|
|
503
|
+
const shouldRemember = !!options?.key;
|
|
517
504
|
const historyKey = options?.key ?? "form:default";
|
|
518
505
|
const historyData = shouldRemember ? router.history.get(historyKey) : void 0;
|
|
519
506
|
const timeoutIds = {
|
|
@@ -543,13 +530,14 @@ function useForm(options) {
|
|
|
543
530
|
function submit(optionsOverrides) {
|
|
544
531
|
const url = typeof options.url === "function" ? options.url() : options.url;
|
|
545
532
|
const data = typeof options.transform === "function" ? options.transform?.(fields) : fields;
|
|
533
|
+
const preserveState = optionsOverrides?.preserveState ?? options.preserveState;
|
|
546
534
|
return router.navigate({
|
|
547
535
|
...options,
|
|
548
536
|
url: url ?? state.context.value?.url,
|
|
549
537
|
method: options.method ?? "POST",
|
|
550
538
|
...optionsOverrides,
|
|
551
539
|
data: safeClone(data),
|
|
552
|
-
preserveState:
|
|
540
|
+
preserveState: preserveState === void 0 && options.method !== "GET" ? true : preserveState,
|
|
553
541
|
hooks: {
|
|
554
542
|
before: (navigation, context) => {
|
|
555
543
|
failed.value = false;
|
|
@@ -557,7 +545,6 @@ function useForm(options) {
|
|
|
557
545
|
recentlySuccessful.value = false;
|
|
558
546
|
clearTimeout(timeoutIds.recentlySuccessful);
|
|
559
547
|
clearTimeout(timeoutIds.recentlyFailed);
|
|
560
|
-
clearErrors();
|
|
561
548
|
return options.hooks?.before?.(navigation, context);
|
|
562
549
|
},
|
|
563
550
|
start: (context) => {
|
|
@@ -576,6 +563,7 @@ function useForm(options) {
|
|
|
576
563
|
return options.hooks?.error?.(incoming, context);
|
|
577
564
|
},
|
|
578
565
|
success: (payload, context) => {
|
|
566
|
+
clearErrors();
|
|
579
567
|
if (options?.reset !== false) {
|
|
580
568
|
reset();
|
|
581
569
|
}
|
|
@@ -600,6 +588,12 @@ function useForm(options) {
|
|
|
600
588
|
clearError(key);
|
|
601
589
|
});
|
|
602
590
|
}
|
|
591
|
+
function hasDirty(...keys) {
|
|
592
|
+
if (keys.length === 0) {
|
|
593
|
+
return isDirty.value;
|
|
594
|
+
}
|
|
595
|
+
return keys.some((key) => !isEqual(toRaw(fields[key]), toRaw(initial[key])));
|
|
596
|
+
}
|
|
603
597
|
function clearError(key) {
|
|
604
598
|
errors.value[key] = void 0;
|
|
605
599
|
}
|
|
@@ -625,6 +619,7 @@ function useForm(options) {
|
|
|
625
619
|
setErrors,
|
|
626
620
|
clearErrors,
|
|
627
621
|
clearError,
|
|
622
|
+
hasDirty,
|
|
628
623
|
submitWithOptions: submit,
|
|
629
624
|
submit: () => submit(),
|
|
630
625
|
hasErrors: computed(() => Object.values(errors.value).length > 0),
|
|
@@ -706,128 +701,4 @@ function defineLayoutProperties(properties) {
|
|
|
706
701
|
state.setViewLayoutProperties(properties);
|
|
707
702
|
}
|
|
708
703
|
|
|
709
|
-
|
|
710
|
-
constructor(name, absolute) {
|
|
711
|
-
this.name = name;
|
|
712
|
-
this.absolute = absolute;
|
|
713
|
-
this.definition = Route.getDefinition(name);
|
|
714
|
-
}
|
|
715
|
-
static getDefinition(name) {
|
|
716
|
-
if (!state.routes.value) {
|
|
717
|
-
throw new Error("Routing is not initialized. Make sure the Vite plugin is enabled and that `virtual:hybridly/router` is imported.");
|
|
718
|
-
}
|
|
719
|
-
const routes = state.routes.value;
|
|
720
|
-
const route = routes?.routes?.[name];
|
|
721
|
-
if (!route) {
|
|
722
|
-
throw new Error(`Route ${name.toString()} does not exist.`);
|
|
723
|
-
}
|
|
724
|
-
return route;
|
|
725
|
-
}
|
|
726
|
-
get template() {
|
|
727
|
-
const origin = !this.absolute ? "" : this.definition.domain ? `${state.routes.value?.url.match(/^\w+:\/\//)?.[0]}${this.definition.domain}${state.routes.value?.port ? `:${state.routes.value?.port}` : ""}` : state.routes.value?.url;
|
|
728
|
-
return `${origin}/${this.definition.uri}`.replace(/\/+$/, "");
|
|
729
|
-
}
|
|
730
|
-
get parameterSegments() {
|
|
731
|
-
return this.template.match(/{[^}?]+\??}/g)?.map((segment) => ({
|
|
732
|
-
name: segment.replace(/{|\??}/g, ""),
|
|
733
|
-
required: !/\?}$/.test(segment)
|
|
734
|
-
})) ?? [];
|
|
735
|
-
}
|
|
736
|
-
matchesUrl(url) {
|
|
737
|
-
if (!this.definition.methods.includes("GET")) {
|
|
738
|
-
return false;
|
|
739
|
-
}
|
|
740
|
-
const pattern = this.template.replace(/(\/?){([^}?]*)(\??)}/g, (_, slash, segment, optional) => {
|
|
741
|
-
const regex = `(?<${segment}>${this.definition.wheres?.[segment]?.replace(/(^\^)|(\$$)/g, "") || "[^/?]+"})`;
|
|
742
|
-
return optional ? `(${slash}${regex})?` : `${slash}${regex}`;
|
|
743
|
-
}).replace(/^\w+:\/\//, "");
|
|
744
|
-
const [location, query] = url.replace(/^\w+:\/\//, "").split("?");
|
|
745
|
-
const matches = new RegExp(`^${pattern}/?$`).exec(location);
|
|
746
|
-
return matches ? { params: matches.groups, query: parse(query) } : false;
|
|
747
|
-
}
|
|
748
|
-
compile(params) {
|
|
749
|
-
const segments = this.parameterSegments;
|
|
750
|
-
if (!segments.length) {
|
|
751
|
-
return this.template;
|
|
752
|
-
}
|
|
753
|
-
return this.template.replace(/{([^}?]+)(\??)}/g, (_, segment, optional) => {
|
|
754
|
-
if (!optional && [null, void 0].includes(params?.[segment])) {
|
|
755
|
-
throw new Error(`Router error: [${segment}] parameter is required for route [${this.name}].`);
|
|
756
|
-
}
|
|
757
|
-
if (segments[segments.length - 1].name === segment && this.definition?.wheres?.[segment] === ".*") {
|
|
758
|
-
return encodeURIComponent(params[segment] ?? "").replace(/%2F/g, "/");
|
|
759
|
-
}
|
|
760
|
-
if (this.definition?.wheres?.[segment] && !new RegExp(`^${optional ? `(${this.definition?.wheres?.[segment]})?` : this.definition?.wheres?.[segment]}$`).test(params[segment] ?? "")) {
|
|
761
|
-
throw new Error(`Router error: [${segment}] parameter does not match required format [${this.definition?.wheres?.[segment]}] for route [${this.name}].`);
|
|
762
|
-
}
|
|
763
|
-
return encodeURIComponent(params[segment] ?? "");
|
|
764
|
-
}).replace(/\/+$/, "");
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
class Router extends String {
|
|
769
|
-
constructor(name, parameters, absolute = true) {
|
|
770
|
-
super();
|
|
771
|
-
this.route = new Route(name, absolute);
|
|
772
|
-
this.setParameters(parameters);
|
|
773
|
-
}
|
|
774
|
-
toString() {
|
|
775
|
-
const unhandled = Object.keys(this.parameters).filter((key) => !this.route.parameterSegments.some(({ name }) => name === key)).filter((key) => key !== "_query").reduce((result, current) => ({ ...result, [current]: this.parameters[current] }), {});
|
|
776
|
-
return this.route.compile(this.parameters) + stringify({ ...unhandled, ...this.parameters._query }, {
|
|
777
|
-
addQueryPrefix: true,
|
|
778
|
-
arrayFormat: "indices",
|
|
779
|
-
encodeValuesOnly: true,
|
|
780
|
-
skipNulls: true,
|
|
781
|
-
encoder: (value, encoder) => typeof value === "boolean" ? Number(value).toString() : encoder(value)
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
static has(name) {
|
|
785
|
-
try {
|
|
786
|
-
Route.getDefinition(name);
|
|
787
|
-
return true;
|
|
788
|
-
} catch {
|
|
789
|
-
return false;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
setParameters(parameters) {
|
|
793
|
-
this.parameters = parameters ?? {};
|
|
794
|
-
this.parameters = ["string", "number"].includes(typeof this.parameters) ? [this.parameters] : this.parameters;
|
|
795
|
-
const segments = this.route.parameterSegments.filter(({ name }) => !state.routes.value?.defaults[name]);
|
|
796
|
-
if (Array.isArray(this.parameters)) {
|
|
797
|
-
this.parameters = this.parameters.reduce((result, current, i) => segments[i] ? { ...result, [segments[i].name]: current } : typeof current === "object" ? { ...result, ...current } : { ...result, [current]: "" }, {});
|
|
798
|
-
} else if (segments.length === 1 && !this.parameters[segments[0].name] && (Reflect.has(this.parameters, Object.values(this.route.definition.bindings)[0]) || Reflect.has(this.parameters, "id"))) {
|
|
799
|
-
this.parameters = { [segments[0].name]: this.parameters };
|
|
800
|
-
}
|
|
801
|
-
this.parameters = {
|
|
802
|
-
...this.getDefaults(),
|
|
803
|
-
...this.substituteBindings()
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
getDefaults() {
|
|
807
|
-
return this.route.parameterSegments.filter(({ name }) => state.routes.value?.defaults[name]).reduce((result, { name }) => ({ ...result, [name]: state.routes.value?.defaults[name] }), {});
|
|
808
|
-
}
|
|
809
|
-
substituteBindings() {
|
|
810
|
-
return Object.entries(this.parameters).reduce((result, [key, value]) => {
|
|
811
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || !this.route.parameterSegments.some(({ name }) => name === key)) {
|
|
812
|
-
return { ...result, [key]: value };
|
|
813
|
-
}
|
|
814
|
-
if (!Reflect.has(value, this.route.definition.bindings[key])) {
|
|
815
|
-
if (Reflect.has(value, "id")) {
|
|
816
|
-
this.route.definition.bindings[key] = "id";
|
|
817
|
-
} else {
|
|
818
|
-
throw new Error(`Router error: object passed as [${key}] parameter is missing route model binding key [${this.route.definition.bindings?.[key]}].`);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
return { ...result, [key]: value[this.route.definition.bindings[key]] };
|
|
822
|
-
}, {});
|
|
823
|
-
}
|
|
824
|
-
valueOf() {
|
|
825
|
-
return this.toString();
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
function route(name, parameters, absolute) {
|
|
830
|
-
return new Router(name, parameters, absolute).toString();
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
export { HybridlyImports, HybridlyResolver, RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, resolvePageComponent, route, useBackForward, useContext, useForm, useHistoryState, usePaginator, useProperties, useProperty, useTypedProperty };
|
|
704
|
+
export { HybridlyImports, HybridlyResolver, RouterLink, defineLayout, defineLayoutProperties, initializeHybridly, resolvePageComponent, useBackForward, useContext, useForm, useHistoryState, usePaginator, useProperties, useProperty, useTypedProperty };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hybridly/vue",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.15",
|
|
4
4
|
"description": "A solution to develop server-driven, client-rendered applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hybridly",
|
|
@@ -38,14 +38,14 @@
|
|
|
38
38
|
"vue": "^3.2.37"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@hybridly/core": "0.0.1-alpha.13",
|
|
42
|
-
"@hybridly/progress-plugin": "0.0.1-alpha.13",
|
|
43
|
-
"@hybridly/utils": "0.0.1-alpha.13",
|
|
44
41
|
"@vue/devtools-api": "^6.4.5",
|
|
45
42
|
"defu": "^6.1.1",
|
|
46
43
|
"lodash.isequal": "^4.5.0",
|
|
47
44
|
"nprogress": "^0.2.0",
|
|
48
|
-
"qs": "^6.11.0"
|
|
45
|
+
"qs": "^6.11.0",
|
|
46
|
+
"@hybridly/core": "0.0.1-alpha.15",
|
|
47
|
+
"@hybridly/progress-plugin": "0.0.1-alpha.15",
|
|
48
|
+
"@hybridly/utils": "0.0.1-alpha.15"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/lodash": "^4.14.190",
|