@studiometa/ui 1.0.0-beta.1 → 1.0.0-rc.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.
Files changed (84) hide show
  1. package/AnchorScrollTo/AnchorScrollTo.d.ts +2 -2
  2. package/AnchorScrollTo/AnchorScrollTo.js +0 -1
  3. package/AnchorScrollTo/AnchorScrollTo.js.map +2 -2
  4. package/Button/Button.twig +12 -9
  5. package/Button/StyledButton.twig +40 -5
  6. package/Button/StyledButtonRounded.twig +12 -42
  7. package/CircularMarquee/CircularMarquee.twig +30 -18
  8. package/Data/DataBind.js +1 -1
  9. package/Data/DataBind.js.map +2 -2
  10. package/Draggable/Draggable.js +7 -2
  11. package/Draggable/Draggable.js.map +2 -2
  12. package/Figure/Figure.d.ts +1 -0
  13. package/Figure/Figure.js.map +2 -2
  14. package/Frame/AbstractFrameTrigger.d.ts +43 -0
  15. package/Frame/AbstractFrameTrigger.js +72 -0
  16. package/Frame/AbstractFrameTrigger.js.map +7 -0
  17. package/Frame/Frame.d.ts +55 -27
  18. package/Frame/Frame.js +132 -138
  19. package/Frame/Frame.js.map +3 -3
  20. package/Frame/FrameAnchor.d.ts +7 -4
  21. package/Frame/FrameAnchor.js +8 -5
  22. package/Frame/FrameAnchor.js.map +2 -2
  23. package/Frame/FrameForm.d.ts +22 -4
  24. package/Frame/FrameForm.js +43 -6
  25. package/Frame/FrameForm.js.map +2 -2
  26. package/Frame/FrameLoader.d.ts +22 -0
  27. package/Frame/FrameLoader.js +22 -0
  28. package/Frame/FrameLoader.js.map +7 -0
  29. package/Frame/FrameTarget.d.ts +6 -17
  30. package/Frame/FrameTarget.js +25 -60
  31. package/Frame/FrameTarget.js.map +2 -2
  32. package/Frame/FrameTriggerLoader.d.ts +13 -0
  33. package/Frame/FrameTriggerLoader.js +13 -0
  34. package/Frame/FrameTriggerLoader.js.map +7 -0
  35. package/Frame/index.d.ts +4 -0
  36. package/Frame/index.js +4 -0
  37. package/Frame/index.js.map +2 -2
  38. package/Frame/types.d.ts +12 -0
  39. package/Frame/types.js +1 -0
  40. package/Frame/types.js.map +7 -0
  41. package/Frame/utils.d.ts +9 -0
  42. package/Frame/utils.js +13 -0
  43. package/Frame/utils.js.map +7 -0
  44. package/LICENSE.md +110 -0
  45. package/Reinsurance/Reinsurance.twig +6 -6
  46. package/Slider/AbstractSliderChild.d.ts +5 -9
  47. package/Slider/AbstractSliderChild.js +0 -11
  48. package/Slider/AbstractSliderChild.js.map +2 -2
  49. package/Slider/Slider.d.ts +23 -30
  50. package/Slider/Slider.js +40 -100
  51. package/Slider/Slider.js.map +2 -2
  52. package/Slider/SliderBtn.d.ts +0 -3
  53. package/Slider/SliderBtn.js +3 -7
  54. package/Slider/SliderBtn.js.map +2 -2
  55. package/Slider/SliderCount.d.ts +0 -2
  56. package/Slider/SliderCount.js +4 -3
  57. package/Slider/SliderCount.js.map +2 -2
  58. package/Slider/SliderDots.d.ts +0 -3
  59. package/Slider/SliderDots.js +6 -7
  60. package/Slider/SliderDots.js.map +2 -2
  61. package/Slider/SliderDrag.d.ts +0 -2
  62. package/Slider/SliderDrag.js +0 -2
  63. package/Slider/SliderDrag.js.map +2 -2
  64. package/Slider/SliderItem.d.ts +10 -26
  65. package/Slider/SliderItem.js +9 -41
  66. package/Slider/SliderItem.js.map +2 -2
  67. package/Tabs/Tabs.twig +73 -9
  68. package/decorators/withTransition.d.ts +4 -0
  69. package/decorators/withTransition.js +40 -45
  70. package/decorators/withTransition.js.map +2 -2
  71. package/index.d.ts +0 -1
  72. package/index.js +0 -1
  73. package/index.js.map +2 -2
  74. package/package.json +4 -3
  75. package/LICENSE +0 -21
  76. package/TableOfContent/TableOfContent.d.ts +0 -39
  77. package/TableOfContent/TableOfContent.js +0 -50
  78. package/TableOfContent/TableOfContent.js.map +0 -7
  79. package/TableOfContent/TableOfContentAnchor.d.ts +0 -36
  80. package/TableOfContent/TableOfContentAnchor.js +0 -54
  81. package/TableOfContent/TableOfContentAnchor.js.map +0 -7
  82. package/TableOfContent/index.d.ts +0 -2
  83. package/TableOfContent/index.js +0 -3
  84. package/TableOfContent/index.js.map +0 -7
package/Frame/Frame.d.ts CHANGED
@@ -3,70 +3,98 @@ import type { BaseProps, BaseConfig } from '@studiometa/js-toolkit';
3
3
  import { FrameAnchor } from './FrameAnchor.js';
4
4
  import { FrameForm } from './FrameForm.js';
5
5
  import { FrameTarget } from './FrameTarget.js';
6
+ import { FrameLoader } from './FrameLoader.js';
7
+ import type { FrameRequestInit, FrameTriggerEvent } from './types.js';
6
8
  export interface FrameProps extends BaseProps {
7
9
  $children: {
8
10
  FrameAnchor: FrameAnchor[];
9
11
  FrameForm: FrameForm[];
10
12
  FrameTarget: FrameTarget[];
11
- Frame: Frame[];
13
+ FrameLoader: FrameLoader[];
12
14
  };
13
15
  $options: {
14
16
  history: boolean;
17
+ requestInit: RequestInit;
18
+ headers: Record<string, string>;
15
19
  };
16
20
  }
17
21
  /**
18
- * Class.
22
+ * Frame class.
23
+ * @see https://ui.studiometa.dev/-/components/Frame/
19
24
  */
20
25
  export declare class Frame<T extends BaseProps = BaseProps> extends Base<T & FrameProps> {
21
26
  /**
22
27
  * Config.
23
28
  */
24
29
  static config: BaseConfig;
30
+ /**
31
+ * DOM Parser to parse the new content to be injected.
32
+ */
33
+ domParser: DOMParser;
34
+ /**
35
+ * Abort controller to prevent multiple simultaneous fetches.
36
+ */
37
+ abortController: AbortController;
38
+ /**
39
+ * Header names.
40
+ */
41
+ headerNames: {
42
+ readonly ACCEPT: "accept";
43
+ readonly X_REQUESTED_BY: "x-requested-by";
44
+ readonly X_TRIGGERED_BY: "x-triggered-by";
45
+ readonly USER_AGENT: "user-agent";
46
+ };
25
47
  /**
26
48
  * Get uniq id.
27
49
  */
28
50
  get id(): string;
29
51
  /**
30
- * Get direct `FrameTarget` children.
52
+ * The client used for the fetch request.
31
53
  */
32
- get directChildrenFrameTarget(): FrameTarget[];
54
+ get client(): typeof fetch;
33
55
  /**
34
- * Prevent scroll top on unload.
56
+ * Default request init.
35
57
  */
36
- onWindowUnload(): void;
58
+ get requestInit(): RequestInit;
37
59
  /**
38
- * Go to the previous URL on `popstate` event.
60
+ * Get chidlren limited to the current instance.
39
61
  */
40
- onWindowPopstate({ event }: {
41
- event: PopStateEvent;
42
- }): void;
62
+ getDirectChildren(name: keyof FrameProps['$children']): any[];
43
63
  /**
44
- * Prevent click on `FrameAnchor`.
64
+ * Fetch new content on frame-trigger.
45
65
  */
46
- onFrameAnchorClick({ target, event }: {
47
- event: MouseEvent;
48
- target: FrameAnchor;
66
+ onFrameAnchorFrameTrigger({ args: [url, requestInit] }: {
67
+ args: FrameTriggerEvent['detail'];
49
68
  }): void;
50
69
  /**
51
- * Prevent submit on forms.
70
+ * Fetch new content on frame-trigger.
52
71
  */
53
- onFrameFormSubmit({ event, target }: {
54
- event: SubmitEvent;
55
- target: FrameForm;
72
+ onFrameFormFrameTrigger({ args: [url, requestInit] }: {
73
+ args: [URL, FrameRequestInit];
56
74
  }): void;
57
75
  /**
58
- * Parge an HTML string into a DOM object.
76
+ * Update content on history back/forward navigation.
77
+ */
78
+ onWindowPopstate(): void;
79
+ /**
80
+ * Trigger FrameLoaders enter.
81
+ */
82
+ onFrameFetchBefore(): void;
83
+ /**
84
+ * Trigger FrameLoaders leave.
85
+ */
86
+ onFrameFetchAfter(): void;
87
+ emitSync(event: string, trigger?: FrameForm | FrameAnchor, ...args: any[]): void;
88
+ /**
89
+ * Fetch given url.
59
90
  */
60
- parseHTML(string?: string): Document;
91
+ fetch(url: URL, requestInit?: FrameRequestInit): Promise<void>;
61
92
  /**
62
- * Go to the given url.
93
+ * Dispatch the contents to update to their matching FrameTarget.
63
94
  */
64
- goTo(url: string, formData?: FormData, scroll?: {
65
- top: number;
66
- left: number;
67
- }): Promise<void>;
95
+ content(url: URL, requestInit: FrameRequestInit, content: string): Promise<void>;
68
96
  /**
69
- * Fetch the given url.
97
+ * Handle errors.
70
98
  */
71
- fetch(url: string, formData?: FormData): Promise<string>;
99
+ error(url: URL, requestInit: FrameRequestInit, error: Error): Promise<void>;
72
100
  }
package/Frame/Frame.js CHANGED
@@ -1,41 +1,46 @@
1
- import { Base, isDirectChild, getDirectChildren } from "@studiometa/js-toolkit";
2
- import { nextFrame, historyPush } from "@studiometa/js-toolkit/utils";
1
+ import { Base, getClosestParent } from "@studiometa/js-toolkit";
2
+ import { domScheduler, historyPush } from "@studiometa/js-toolkit/utils";
3
3
  import { FrameAnchor } from "./FrameAnchor.js";
4
4
  import { FrameForm } from "./FrameForm.js";
5
5
  import { FrameTarget } from "./FrameTarget.js";
6
- function getScrollPosition() {
7
- return {
8
- left: window.pageXOffset,
9
- top: window.pageYOffset
10
- };
11
- }
12
- const cache = /* @__PURE__ */ new Map();
6
+ import { FrameLoader } from "./FrameLoader.js";
7
+ import { EVENTS } from "./utils.js";
13
8
  class Frame extends Base {
14
9
  /**
15
10
  * Config.
16
11
  */
17
12
  static config = {
18
13
  name: "Frame",
19
- emits: [
20
- "before-fetch",
21
- "after-fetch",
22
- "before-leave",
23
- "after-leave",
24
- "before-content",
25
- "after-content",
26
- "before-enter",
27
- "after-enter"
28
- ],
14
+ emits: Object.values(EVENTS),
29
15
  components: {
30
16
  FrameAnchor,
31
17
  FrameForm,
32
18
  FrameTarget,
33
- Frame
19
+ FrameLoader
34
20
  },
35
21
  options: {
36
- history: Boolean
22
+ history: Boolean,
23
+ requestInit: Object,
24
+ headers: Object
37
25
  }
38
26
  };
27
+ /**
28
+ * DOM Parser to parse the new content to be injected.
29
+ */
30
+ domParser = new DOMParser();
31
+ /**
32
+ * Abort controller to prevent multiple simultaneous fetches.
33
+ */
34
+ abortController = new AbortController();
35
+ /**
36
+ * Header names.
37
+ */
38
+ headerNames = {
39
+ ACCEPT: "accept",
40
+ X_REQUESTED_BY: "x-requested-by",
41
+ X_TRIGGERED_BY: "x-triggered-by",
42
+ USER_AGENT: "user-agent"
43
+ };
39
44
  /**
40
45
  * Get uniq id.
41
46
  */
@@ -43,151 +48,140 @@ class Frame extends Base {
43
48
  return this.$el.id;
44
49
  }
45
50
  /**
46
- * Get direct `FrameTarget` children.
51
+ * The client used for the fetch request.
47
52
  */
48
- get directChildrenFrameTarget() {
49
- return getDirectChildren(this, "Frame", "FrameTarget");
53
+ get client() {
54
+ return window.fetch.bind(window);
50
55
  }
51
56
  /**
52
- * Prevent scroll top on unload.
57
+ * Default request init.
53
58
  */
54
- onWindowUnload() {
55
- const { history } = window;
56
- if (!history.state) {
57
- return;
58
- }
59
- history.replaceState(
60
- {
61
- ...history.state,
62
- scroll: getScrollPosition()
63
- },
64
- ""
65
- );
59
+ get requestInit() {
60
+ const { headerNames } = this;
61
+ const { requestInit, headers } = this.$options;
62
+ const requestedBy = "@studiometa/ui/Frame";
63
+ return {
64
+ ...requestInit,
65
+ headers: {
66
+ [headerNames.ACCEPT]: "text/*",
67
+ [headerNames.X_REQUESTED_BY]: requestedBy,
68
+ [headerNames.USER_AGENT]: `${navigator.userAgent} ${requestedBy}`,
69
+ ...requestInit.headers,
70
+ ...headers
71
+ }
72
+ };
66
73
  }
67
74
  /**
68
- * Go to the previous URL on `popstate` event.
75
+ * Get chidlren limited to the current instance.
69
76
  */
70
- onWindowPopstate({ event }) {
71
- this.goTo(window.location.href, null, event.state);
77
+ getDirectChildren(name) {
78
+ const children = [];
79
+ for (const child of this.$children[name]) {
80
+ if (getClosestParent(child, this.constructor) === this) {
81
+ children.push(child);
82
+ }
83
+ }
84
+ return children;
72
85
  }
73
86
  /**
74
- * Prevent click on `FrameAnchor`.
87
+ * Fetch new content on frame-trigger.
75
88
  */
76
- onFrameAnchorClick({ target, event }) {
77
- if (!isDirectChild(this, "Frame", "FrameAnchor", target)) {
78
- return;
79
- }
80
- this.$log("onAFrameClick", target, event);
81
- event.preventDefault();
82
- if (target.href === window.location.href) {
83
- return;
84
- }
85
- this.goTo(target.href);
89
+ onFrameAnchorFrameTrigger({ args: [url, requestInit] }) {
90
+ this.fetch(url, requestInit);
86
91
  }
87
92
  /**
88
- * Prevent submit on forms.
93
+ * Fetch new content on frame-trigger.
89
94
  */
90
- onFrameFormSubmit({ event, target }) {
91
- if (!isDirectChild(this, "Frame", "FrameForm", target)) {
92
- return;
93
- }
94
- this.$log("onFrameFormFrameSubmit", target, event);
95
- event.preventDefault();
96
- const url = new URL(target.action);
97
- if (target.$el.method === "get") {
98
- url.search = new URLSearchParams(new FormData(target.$el)).toString();
99
- this.goTo(url.toString());
100
- }
101
- if (target.$el.method === "post") {
102
- this.goTo(url.toString(), new FormData(target.$el));
103
- }
95
+ onFrameFormFrameTrigger({ args: [url, requestInit] }) {
96
+ this.fetch(url, requestInit);
104
97
  }
105
98
  /**
106
- * Parge an HTML string into a DOM object.
99
+ * Update content on history back/forward navigation.
107
100
  */
108
- parseHTML(string = "") {
109
- return new DOMParser().parseFromString(string, "text/html");
101
+ onWindowPopstate() {
102
+ this.fetch(new URL(window.location.href), {
103
+ headers: {
104
+ [this.headerNames.X_TRIGGERED_BY]: "popstate"
105
+ }
106
+ });
110
107
  }
111
108
  /**
112
- * Go to the given url.
109
+ * Trigger FrameLoaders enter.
113
110
  */
114
- async goTo(url, formData = null, scroll = null) {
115
- this.$log("goTo", url);
116
- const parsedUrl = new URL(url);
117
- if (parsedUrl.origin !== window.location.origin) {
118
- throw new Error("Cross origin request are not allowed.");
119
- }
120
- this.$emit("before-fetch", url);
121
- const content = await this.fetch(url, formData);
122
- const doc = this.parseHTML(content);
123
- const el = doc.querySelector(`#${this.id}`);
124
- const newFrame = new Frame(el);
125
- newFrame.__children.registerAll();
126
- this.$emit("after-fetch", url, content);
127
- this.$emit("before-leave");
128
- await Promise.all(this.directChildrenFrameTarget.map((target) => target.leave()));
129
- this.$emit("after-leave");
130
- this.$emit("before-content");
131
- this.directChildrenFrameTarget.map(
132
- (target, index) => target.updateContent(newFrame.directChildrenFrameTarget[index])
133
- );
134
- if (this.$options.history && formData === null) {
135
- document.title = doc.title;
136
- historyPush({ path: parsedUrl.pathname, search: parsedUrl.searchParams });
111
+ onFrameFetchBefore() {
112
+ for (const loader of this.getDirectChildren("FrameLoader")) {
113
+ loader.enter();
137
114
  }
138
- if (scroll) {
139
- document.scrollingElement.scrollTop = scroll.top;
140
- document.scrollingElement.scrollLeft = scroll.left;
115
+ }
116
+ /**
117
+ * Trigger FrameLoaders leave.
118
+ */
119
+ onFrameFetchAfter() {
120
+ for (const loader of this.getDirectChildren("FrameLoader")) {
121
+ loader.leave();
141
122
  }
142
- await nextFrame();
143
- this.$root.$update();
144
- await nextFrame();
145
- this.$emit("after-content");
146
- this.$emit("before-enter");
147
- await Promise.all(this.directChildrenFrameTarget.map((target) => target.enter()));
148
- this.$emit("after-enter");
123
+ }
124
+ emitSync(event, trigger = null, ...args) {
125
+ this.$emit(event, ...args);
126
+ trigger?.$emit(event, ...args);
149
127
  }
150
128
  /**
151
- * Fetch the given url.
129
+ * Fetch given url.
152
130
  */
153
- async fetch(url, formData = null) {
154
- if (formData) {
155
- const promise2 = fetch(url, {
156
- method: "post",
157
- body: formData
158
- }).then((response) => response.text());
159
- const content = await promise2;
160
- return content;
131
+ async fetch(url, requestInit = {}) {
132
+ this.emitSync(EVENTS.FETCH_BEFORE, requestInit.trigger, url, requestInit);
133
+ this.abortController.abort();
134
+ this.abortController = new AbortController();
135
+ const init = {
136
+ ...this.requestInit,
137
+ ...requestInit,
138
+ headers: {
139
+ ...this.requestInit.headers,
140
+ ...requestInit.headers
141
+ },
142
+ signal: this.abortController.signal
143
+ };
144
+ this.$log("fetch", url, init);
145
+ this.emitSync(EVENTS.FETCH, init.trigger, url, init);
146
+ try {
147
+ const content = await this.client(url, init).then((response) => response.text());
148
+ this.emitSync(EVENTS.FETCH_AFTER, init.trigger, url, requestInit, content);
149
+ this.content(url, init, content);
150
+ } catch (error) {
151
+ this.emitSync(EVENTS.FETCH_AFTER, init.trigger, url, requestInit, error);
152
+ this.error(url, init, error);
161
153
  }
162
- const cached = cache.get(url);
163
- if (cached) {
164
- if (cached.status === "pending") {
165
- return cached.promise;
154
+ }
155
+ /**
156
+ * Dispatch the contents to update to their matching FrameTarget.
157
+ */
158
+ async content(url, requestInit, content) {
159
+ this.$log("content", url, content);
160
+ this.emitSync(EVENTS.CONTENT, requestInit.trigger, url, requestInit, content);
161
+ const doc = this.domParser.parseFromString(content, "text/html");
162
+ const el = doc.querySelector(`#${this.id}`) ?? doc;
163
+ const promises = [];
164
+ if (this.$options.history) {
165
+ if (requestInit?.headers?.[this.headerNames.X_TRIGGERED_BY] !== "popstate") {
166
+ historyPush({ path: url.pathname, search: url.searchParams });
166
167
  }
167
- return cached.content;
168
- }
169
- const promise = fetch(url).then((response) => response.text());
170
- try {
171
- cache.set(url, {
172
- promise,
173
- status: "pending",
174
- content: void 0
175
- });
176
- const content = await promise;
177
- cache.set(url, {
178
- promise,
179
- status: "resolved",
180
- content
181
- });
182
- return content;
183
- } catch (err) {
184
- cache.set(url, {
185
- promise,
186
- status: "error",
187
- content: err
168
+ domScheduler.write(() => {
169
+ if (doc.title) document.title = doc.title;
188
170
  });
189
- return err;
190
171
  }
172
+ for (const frameTarget of this.getDirectChildren("FrameTarget")) {
173
+ promises.push(frameTarget.updateContent(el.querySelector(`#${frameTarget.id}`)));
174
+ }
175
+ await Promise.all(promises);
176
+ this.emitSync(EVENTS.CONTENT_AFTER, requestInit.trigger, url, requestInit, content);
177
+ await this.$root.$update();
178
+ }
179
+ /**
180
+ * Handle errors.
181
+ */
182
+ async error(url, requestInit, error) {
183
+ this.$log("error", url, requestInit, error);
184
+ this.emitSync(EVENTS.ERROR, requestInit.trigger, url, requestInit, error);
191
185
  }
192
186
  }
193
187
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../packages/ui/Frame/Frame.ts"],
4
- "sourcesContent": ["import { Base, isDirectChild, getDirectChildren } from '@studiometa/js-toolkit';\nimport type { BaseProps, BaseConfig } from '@studiometa/js-toolkit';\nimport { nextFrame, historyPush } from '@studiometa/js-toolkit/utils';\nimport { FrameAnchor } from './FrameAnchor.js';\nimport { FrameForm } from './FrameForm.js';\nimport { FrameTarget } from './FrameTarget.js';\n\n/**\n * Get the scroll position.\n */\nfunction getScrollPosition() {\n return {\n left: window.pageXOffset,\n top: window.pageYOffset,\n };\n}\n\n/**\n * The fetch cache.\n */\nconst cache: Map<\n string,\n { promise: Promise<string>; status: 'pending' | 'resolved' | 'error'; content: string }\n> = new Map();\n\nexport interface FrameProps extends BaseProps {\n $children: {\n FrameAnchor: FrameAnchor[];\n FrameForm: FrameForm[];\n FrameTarget: FrameTarget[];\n // eslint-disable-next-line no-use-before-define\n Frame: Frame[];\n };\n $options: {\n history: boolean;\n };\n}\n\n/**\n * Class.\n */\nexport class Frame<T extends BaseProps = BaseProps> extends Base<T & FrameProps> {\n /**\n * Config.\n */\n static config: BaseConfig = {\n name: 'Frame',\n emits: [\n 'before-fetch',\n 'after-fetch',\n 'before-leave',\n 'after-leave',\n 'before-content',\n 'after-content',\n 'before-enter',\n 'after-enter',\n ],\n components: {\n FrameAnchor,\n FrameForm,\n FrameTarget,\n Frame,\n },\n options: {\n history: Boolean,\n },\n };\n\n /**\n * Get uniq id.\n */\n get id() {\n return this.$el.id;\n }\n\n /**\n * Get direct `FrameTarget` children.\n */\n get directChildrenFrameTarget(): FrameTarget[] {\n return getDirectChildren(this, 'Frame', 'FrameTarget');\n }\n\n /**\n * Prevent scroll top on unload.\n */\n onWindowUnload() {\n const { history } = window;\n\n if (!history.state) {\n return;\n }\n\n history.replaceState(\n {\n ...history.state,\n scroll: getScrollPosition(),\n },\n '',\n );\n }\n\n /**\n * Go to the previous URL on `popstate` event.\n */\n onWindowPopstate({ event }: { event: PopStateEvent }) {\n this.goTo(window.location.href, null, event.state);\n }\n\n /**\n * Prevent click on `FrameAnchor`.\n */\n onFrameAnchorClick({ target, event }: { event: MouseEvent, target: FrameAnchor }) {\n // Prevent propagation of nested frames\n if (!isDirectChild(this, 'Frame', 'FrameAnchor', target)) {\n return;\n }\n\n this.$log('onAFrameClick', target, event);\n event.preventDefault();\n\n // Do nothing when clicking links on the same page\n // @todo handle hash change\n if (target.href === window.location.href) {\n return;\n }\n\n this.goTo(target.href);\n }\n\n /**\n * Prevent submit on forms.\n */\n onFrameFormSubmit({ event, target }: { event: SubmitEvent, target: FrameForm }) {\n // Prevent propagation of nested frames\n if (!isDirectChild(this, 'Frame', 'FrameForm', target)) {\n return;\n }\n\n this.$log('onFrameFormFrameSubmit', target, event);\n event.preventDefault();\n const url = new URL(target.action);\n\n if (target.$el.method === 'get') {\n // @ts-ignore\n url.search = new URLSearchParams(new FormData(target.$el)).toString();\n this.goTo(url.toString());\n }\n\n if (target.$el.method === 'post') {\n this.goTo(url.toString(), new FormData(target.$el));\n }\n }\n\n /**\n * Parge an HTML string into a DOM object.\n */\n parseHTML(string = '') {\n return new DOMParser().parseFromString(string, 'text/html');\n }\n\n /**\n * Go to the given url.\n */\n async goTo(url: string, formData: FormData = null, scroll: { top: number; left: number } = null) {\n this.$log('goTo', url);\n const parsedUrl = new URL(url);\n\n if (parsedUrl.origin !== window.location.origin) {\n throw new Error('Cross origin request are not allowed.');\n }\n\n this.$emit('before-fetch', url);\n\n // @todo add option to use content as is or to parse it and extract the new frame\n const content = await this.fetch(url, formData);\n const doc = this.parseHTML(content);\n const el = doc.querySelector(`#${this.id}`);\n // @todo manage el === null\n const newFrame = new Frame(el as HTMLElement);\n newFrame.__children.registerAll();\n\n this.$emit('after-fetch', url, content);\n\n this.$emit('before-leave');\n // Leave all\n await Promise.all(this.directChildrenFrameTarget.map((target) => target.leave()));\n\n this.$emit('after-leave');\n this.$emit('before-content');\n\n // Update content\n // @todo insert non existing FrameTarget as well\n this.directChildrenFrameTarget.map((target, index) =>\n target.updateContent(newFrame.directChildrenFrameTarget[index]),\n );\n\n // Push history\n if (this.$options.history && formData === null) {\n document.title = doc.title;\n historyPush({ path: parsedUrl.pathname, search: parsedUrl.searchParams });\n }\n\n if (scroll) {\n document.scrollingElement.scrollTop = scroll.top;\n document.scrollingElement.scrollLeft = scroll.left;\n }\n\n // Update components\n await nextFrame();\n this.$root.$update();\n await nextFrame();\n\n this.$emit('after-content');\n this.$emit('before-enter');\n\n // Enter all\n await Promise.all(this.directChildrenFrameTarget.map((target) => target.enter()));\n\n this.$emit('after-enter');\n }\n\n /**\n * Fetch the given url.\n */\n async fetch(url: string, formData: FormData = null): Promise<string> {\n // @note skip cache for POST requests.\n if (formData) {\n const promise = fetch(url, {\n method: 'post',\n body: formData,\n }).then((response) => response.text());\n\n const content = await promise;\n return content;\n }\n\n const cached = cache.get(url);\n\n if (cached) {\n if (cached.status === 'pending') {\n return cached.promise;\n }\n\n return cached.content;\n }\n\n const promise = fetch(url).then((response) => response.text());\n\n try {\n cache.set(url, {\n promise,\n status: 'pending',\n content: undefined,\n });\n\n const content = await promise;\n\n cache.set(url, {\n promise,\n status: 'resolved',\n content,\n });\n\n return content;\n } catch (err) {\n cache.set(url, {\n promise,\n status: 'error',\n content: err,\n });\n\n return err;\n }\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,MAAM,eAAe,yBAAyB;AAEvD,SAAS,WAAW,mBAAmB;AACvC,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAK5B,SAAS,oBAAoB;AAC3B,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,KAAK,OAAO;AAAA,EACd;AACF;AAKA,MAAM,QAGF,oBAAI,IAAI;AAkBL,MAAM,cAA+C,KAAqB;AAAA;AAAA;AAAA;AAAA,EAI/E,OAAO,SAAqB;AAAA,IAC1B,MAAM;AAAA,IACN,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,SAAS;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,KAAK;AACP,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,4BAA2C;AAC7C,WAAO,kBAAkB,MAAM,SAAS,aAAa;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB;AACf,UAAM,EAAE,QAAQ,IAAI;AAEpB,QAAI,CAAC,QAAQ,OAAO;AAClB;AAAA,IACF;AAEA,YAAQ;AAAA,MACN;AAAA,QACE,GAAG,QAAQ;AAAA,QACX,QAAQ,kBAAkB;AAAA,MAC5B;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,EAAE,MAAM,GAA6B;AACpD,SAAK,KAAK,OAAO,SAAS,MAAM,MAAM,MAAM,KAAK;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,EAAE,QAAQ,MAAM,GAA+C;AAEhF,QAAI,CAAC,cAAc,MAAM,SAAS,eAAe,MAAM,GAAG;AACxD;AAAA,IACF;AAEA,SAAK,KAAK,iBAAiB,QAAQ,KAAK;AACxC,UAAM,eAAe;AAIrB,QAAI,OAAO,SAAS,OAAO,SAAS,MAAM;AACxC;AAAA,IACF;AAEA,SAAK,KAAK,OAAO,IAAI;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAkB,EAAE,OAAO,OAAO,GAA8C;AAE9E,QAAI,CAAC,cAAc,MAAM,SAAS,aAAa,MAAM,GAAG;AACtD;AAAA,IACF;AAEA,SAAK,KAAK,0BAA0B,QAAQ,KAAK;AACjD,UAAM,eAAe;AACrB,UAAM,MAAM,IAAI,IAAI,OAAO,MAAM;AAEjC,QAAI,OAAO,IAAI,WAAW,OAAO;AAE/B,UAAI,SAAS,IAAI,gBAAgB,IAAI,SAAS,OAAO,GAAG,CAAC,EAAE,SAAS;AACpE,WAAK,KAAK,IAAI,SAAS,CAAC;AAAA,IAC1B;AAEA,QAAI,OAAO,IAAI,WAAW,QAAQ;AAChC,WAAK,KAAK,IAAI,SAAS,GAAG,IAAI,SAAS,OAAO,GAAG,CAAC;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAS,IAAI;AACrB,WAAO,IAAI,UAAU,EAAE,gBAAgB,QAAQ,WAAW;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAK,KAAa,WAAqB,MAAM,SAAwC,MAAM;AAC/F,SAAK,KAAK,QAAQ,GAAG;AACrB,UAAM,YAAY,IAAI,IAAI,GAAG;AAE7B,QAAI,UAAU,WAAW,OAAO,SAAS,QAAQ;AAC/C,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAEA,SAAK,MAAM,gBAAgB,GAAG;AAG9B,UAAM,UAAU,MAAM,KAAK,MAAM,KAAK,QAAQ;AAC9C,UAAM,MAAM,KAAK,UAAU,OAAO;AAClC,UAAM,KAAK,IAAI,cAAc,IAAI,KAAK,EAAE,EAAE;AAE1C,UAAM,WAAW,IAAI,MAAM,EAAiB;AAC5C,aAAS,WAAW,YAAY;AAEhC,SAAK,MAAM,eAAe,KAAK,OAAO;AAEtC,SAAK,MAAM,cAAc;AAEzB,UAAM,QAAQ,IAAI,KAAK,0BAA0B,IAAI,CAAC,WAAW,OAAO,MAAM,CAAC,CAAC;AAEhF,SAAK,MAAM,aAAa;AACxB,SAAK,MAAM,gBAAgB;AAI3B,SAAK,0BAA0B;AAAA,MAAI,CAAC,QAAQ,UAC1C,OAAO,cAAc,SAAS,0BAA0B,KAAK,CAAC;AAAA,IAChE;AAGA,QAAI,KAAK,SAAS,WAAW,aAAa,MAAM;AAC9C,eAAS,QAAQ,IAAI;AACrB,kBAAY,EAAE,MAAM,UAAU,UAAU,QAAQ,UAAU,aAAa,CAAC;AAAA,IAC1E;AAEA,QAAI,QAAQ;AACV,eAAS,iBAAiB,YAAY,OAAO;AAC7C,eAAS,iBAAiB,aAAa,OAAO;AAAA,IAChD;AAGA,UAAM,UAAU;AAChB,SAAK,MAAM,QAAQ;AACnB,UAAM,UAAU;AAEhB,SAAK,MAAM,eAAe;AAC1B,SAAK,MAAM,cAAc;AAGzB,UAAM,QAAQ,IAAI,KAAK,0BAA0B,IAAI,CAAC,WAAW,OAAO,MAAM,CAAC,CAAC;AAEhF,SAAK,MAAM,aAAa;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,KAAa,WAAqB,MAAuB;AAEnE,QAAI,UAAU;AACZ,YAAMA,WAAU,MAAM,KAAK;AAAA,QACzB,QAAQ;AAAA,QACR,MAAM;AAAA,MACR,CAAC,EAAE,KAAK,CAAC,aAAa,SAAS,KAAK,CAAC;AAErC,YAAM,UAAU,MAAMA;AACtB,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,MAAM,IAAI,GAAG;AAE5B,QAAI,QAAQ;AACV,UAAI,OAAO,WAAW,WAAW;AAC/B,eAAO,OAAO;AAAA,MAChB;AAEA,aAAO,OAAO;AAAA,IAChB;AAEA,UAAM,UAAU,MAAM,GAAG,EAAE,KAAK,CAAC,aAAa,SAAS,KAAK,CAAC;AAE7D,QAAI;AACF,YAAM,IAAI,KAAK;AAAA,QACb;AAAA,QACA,QAAQ;AAAA,QACR,SAAS;AAAA,MACX,CAAC;AAED,YAAM,UAAU,MAAM;AAEtB,YAAM,IAAI,KAAK;AAAA,QACb;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,YAAM,IAAI,KAAK;AAAA,QACb;AAAA,QACA,QAAQ;AAAA,QACR,SAAS;AAAA,MACX,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
6
- "names": ["promise"]
4
+ "sourcesContent": ["import { Base, getClosestParent } from '@studiometa/js-toolkit';\nimport type { BaseProps, BaseConfig } from '@studiometa/js-toolkit';\nimport { domScheduler, historyPush } from '@studiometa/js-toolkit/utils';\nimport { FrameAnchor } from './FrameAnchor.js';\nimport { FrameForm } from './FrameForm.js';\nimport { FrameTarget } from './FrameTarget.js';\nimport { FrameLoader } from './FrameLoader.js';\nimport type { FrameRequestInit, FrameTriggerEvent } from './types.js';\nimport { EVENTS } from './utils.js';\n\nexport interface FrameProps extends BaseProps {\n $children: {\n FrameAnchor: FrameAnchor[];\n FrameForm: FrameForm[];\n FrameTarget: FrameTarget[];\n FrameLoader: FrameLoader[];\n };\n $options: {\n history: boolean;\n requestInit: RequestInit;\n headers: Record<string, string>;\n };\n}\n\n/**\n * Frame class.\n * @see https://ui.studiometa.dev/-/components/Frame/\n */\nexport class Frame<T extends BaseProps = BaseProps> extends Base<T & FrameProps> {\n /**\n * Config.\n */\n static config: BaseConfig = {\n name: 'Frame',\n emits: Object.values(EVENTS),\n components: {\n FrameAnchor,\n FrameForm,\n FrameTarget,\n FrameLoader,\n },\n options: {\n history: Boolean,\n requestInit: Object,\n headers: Object,\n },\n };\n\n /**\n * DOM Parser to parse the new content to be injected.\n */\n domParser = new DOMParser();\n\n /**\n * Abort controller to prevent multiple simultaneous fetches.\n */\n abortController = new AbortController();\n\n /**\n * Header names.\n */\n headerNames = {\n ACCEPT: 'accept',\n X_REQUESTED_BY: 'x-requested-by',\n X_TRIGGERED_BY: 'x-triggered-by',\n USER_AGENT: 'user-agent',\n } as const;\n\n /**\n * Get uniq id.\n */\n get id() {\n return this.$el.id;\n }\n\n /**\n * The client used for the fetch request.\n */\n get client(): typeof fetch {\n return window.fetch.bind(window);\n }\n\n /**\n * Default request init.\n */\n get requestInit(): RequestInit {\n const { headerNames } = this;\n const { requestInit, headers } = this.$options;\n const requestedBy = '@studiometa/ui/Frame';\n\n return {\n ...requestInit,\n headers: {\n [headerNames.ACCEPT]: 'text/*',\n [headerNames.X_REQUESTED_BY]: requestedBy,\n [headerNames.USER_AGENT]: `${navigator.userAgent} ${requestedBy}`,\n ...requestInit.headers,\n ...headers,\n },\n };\n }\n\n /**\n * Get chidlren limited to the current instance.\n */\n getDirectChildren(name: keyof FrameProps['$children']) {\n const children = [];\n for (const child of this.$children[name]) {\n if (getClosestParent(child, this.constructor) === this) {\n children.push(child);\n }\n }\n return children;\n }\n\n /**\n * Fetch new content on frame-trigger.\n */\n onFrameAnchorFrameTrigger({ args: [url, requestInit] }: { args: FrameTriggerEvent['detail'] }) {\n this.fetch(url, requestInit);\n }\n\n /**\n * Fetch new content on frame-trigger.\n */\n onFrameFormFrameTrigger({ args: [url, requestInit] }: { args: [URL, FrameRequestInit] }) {\n this.fetch(url, requestInit);\n }\n\n /**\n * Update content on history back/forward navigation.\n */\n onWindowPopstate() {\n this.fetch(new URL(window.location.href), {\n headers: {\n [this.headerNames.X_TRIGGERED_BY]: 'popstate',\n },\n });\n }\n\n /**\n * Trigger FrameLoaders enter.\n */\n onFrameFetchBefore() {\n for (const loader of this.getDirectChildren('FrameLoader')) {\n loader.enter();\n }\n }\n\n /**\n * Trigger FrameLoaders leave.\n */\n onFrameFetchAfter() {\n for (const loader of this.getDirectChildren('FrameLoader')) {\n loader.leave();\n }\n }\n\n emitSync(event: string, trigger: FrameForm | FrameAnchor = null, ...args: any[]) {\n this.$emit(event, ...args);\n trigger?.$emit(event, ...args);\n }\n\n /**\n * Fetch given url.\n */\n async fetch(url: URL, requestInit: FrameRequestInit = {}) {\n this.emitSync(EVENTS.FETCH_BEFORE, requestInit.trigger, url, requestInit);\n\n this.abortController.abort();\n this.abortController = new AbortController();\n const init = {\n ...this.requestInit,\n ...requestInit,\n headers: {\n ...this.requestInit.headers,\n ...requestInit.headers,\n },\n signal: this.abortController.signal,\n };\n\n this.$log('fetch', url, init);\n this.emitSync(EVENTS.FETCH, init.trigger, url, init);\n\n try {\n const content = await this.client(url, init).then((response) => response.text());\n this.emitSync(EVENTS.FETCH_AFTER, init.trigger, url, requestInit, content);\n this.content(url, init, content);\n } catch (error) {\n this.emitSync(EVENTS.FETCH_AFTER, init.trigger, url, requestInit, error);\n this.error(url, init, error);\n }\n }\n\n /**\n * Dispatch the contents to update to their matching FrameTarget.\n */\n async content(url: URL, requestInit: FrameRequestInit, content: string) {\n this.$log('content', url, content);\n this.emitSync(EVENTS.CONTENT, requestInit.trigger, url, requestInit, content);\n\n const doc = this.domParser.parseFromString(content, 'text/html');\n const el = doc.querySelector(`#${this.id}`) ?? doc;\n const promises = [];\n\n // @todo inject styles and scripts from new <head>\n if (this.$options.history) {\n if (requestInit?.headers?.[this.headerNames.X_TRIGGERED_BY] !== 'popstate') {\n historyPush({ path: url.pathname, search: url.searchParams });\n }\n domScheduler.write(() => {\n if (doc.title) document.title = doc.title;\n });\n }\n\n for (const frameTarget of this.getDirectChildren('FrameTarget')) {\n promises.push(frameTarget.updateContent(el.querySelector(`#${frameTarget.id}`)));\n }\n\n await Promise.all(promises);\n\n this.emitSync(EVENTS.CONTENT_AFTER, requestInit.trigger, url, requestInit, content);\n\n // We need to update the root instance to make sure newly inserted\n // components are correctly detected and mounted. This avoid having\n // to declare all potentials component as children of the Frame component.\n await this.$root.$update();\n }\n\n /**\n * Handle errors.\n */\n async error(url: URL, requestInit: FrameRequestInit, error: Error) {\n this.$log('error', url, requestInit, error);\n this.emitSync(EVENTS.ERROR, requestInit.trigger, url, requestInit, error);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
7
  }
@@ -1,18 +1,21 @@
1
- import { Base } from '@studiometa/js-toolkit';
2
1
  import type { BaseConfig, BaseProps } from '@studiometa/js-toolkit';
2
+ import { AbstractFrameTrigger } from './AbstractFrameTrigger.js';
3
3
  export interface FrameAnchorProps extends BaseProps {
4
4
  $el: HTMLAnchorElement;
5
5
  }
6
6
  /**
7
7
  * FrameAnchor class.
8
8
  */
9
- export declare class FrameAnchor<T extends BaseProps = BaseProps> extends Base<T & FrameAnchorProps> {
9
+ export declare class FrameAnchor<T extends BaseProps = BaseProps> extends AbstractFrameTrigger<T & FrameAnchorProps> {
10
10
  /**
11
11
  * Config.
12
12
  */
13
13
  static config: BaseConfig;
14
14
  /**
15
- * Get the URL.
15
+ * Prevent click.
16
16
  */
17
- get href(): string;
17
+ onClick({ event }: {
18
+ event: MouseEvent;
19
+ target: FrameAnchor;
20
+ }): void;
18
21
  }
@@ -1,5 +1,5 @@
1
- import { Base } from "@studiometa/js-toolkit";
2
- class FrameAnchor extends Base {
1
+ import { AbstractFrameTrigger } from "./AbstractFrameTrigger.js";
2
+ class FrameAnchor extends AbstractFrameTrigger {
3
3
  /**
4
4
  * Config.
5
5
  */
@@ -7,10 +7,13 @@ class FrameAnchor extends Base {
7
7
  name: "FrameAnchor"
8
8
  };
9
9
  /**
10
- * Get the URL.
10
+ * Prevent click.
11
11
  */
12
- get href() {
13
- return this.$el.href;
12
+ onClick({ event }) {
13
+ if (!event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey && event.button === 0 && this.$el.target !== "_blank") {
14
+ event.preventDefault();
15
+ this.trigger();
16
+ }
14
17
  }
15
18
  }
16
19
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../packages/ui/Frame/FrameAnchor.ts"],
4
- "sourcesContent": ["import { Base } from '@studiometa/js-toolkit';\nimport type { BaseConfig, BaseProps } from '@studiometa/js-toolkit';\n\nexport interface FrameAnchorProps extends BaseProps {\n $el: HTMLAnchorElement;\n}\n\n/**\n * FrameAnchor class.\n */\nexport class FrameAnchor<T extends BaseProps = BaseProps> extends Base<T & FrameAnchorProps> {\n /**\n * Config.\n */\n static config: BaseConfig = {\n name: 'FrameAnchor',\n };\n\n /**\n * Get the URL.\n */\n get href(): string {\n return this.$el.href;\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,YAAY;AAUd,MAAM,oBAAqD,KAA2B;AAAA;AAAA;AAAA;AAAA,EAI3F,OAAO,SAAqB;AAAA,IAC1B,MAAM;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,OAAe;AACjB,WAAO,KAAK,IAAI;AAAA,EAClB;AACF;",
4
+ "sourcesContent": ["import type { BaseConfig, BaseProps } from '@studiometa/js-toolkit';\nimport { AbstractFrameTrigger } from './AbstractFrameTrigger.js';\n\nexport interface FrameAnchorProps extends BaseProps {\n $el: HTMLAnchorElement;\n}\n\n/**\n * FrameAnchor class.\n */\nexport class FrameAnchor<T extends BaseProps = BaseProps> extends AbstractFrameTrigger<\n T & FrameAnchorProps\n> {\n /**\n * Config.\n */\n static config: BaseConfig = {\n name: 'FrameAnchor',\n };\n\n /**\n * Prevent click.\n */\n onClick({ event }: { event: MouseEvent; target: FrameAnchor }) {\n if (\n !event.ctrlKey &&\n !event.shiftKey &&\n !event.altKey &&\n !event.metaKey &&\n event.button === 0 &&\n this.$el.target !== '_blank'\n ) {\n event.preventDefault();\n this.trigger();\n }\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,4BAA4B;AAS9B,MAAM,oBAAqD,qBAEhE;AAAA;AAAA;AAAA;AAAA,EAIA,OAAO,SAAqB;AAAA,IAC1B,MAAM;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,EAAE,MAAM,GAA+C;AAC7D,QACE,CAAC,MAAM,WACP,CAAC,MAAM,YACP,CAAC,MAAM,UACP,CAAC,MAAM,WACP,MAAM,WAAW,KACjB,KAAK,IAAI,WAAW,UACpB;AACA,YAAM,eAAe;AACrB,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,18 +1,36 @@
1
- import { Base } from '@studiometa/js-toolkit';
2
1
  import type { BaseProps, BaseConfig } from '@studiometa/js-toolkit';
2
+ import { AbstractFrameTrigger } from './AbstractFrameTrigger.js';
3
3
  export interface FrameFormProps extends BaseProps {
4
4
  $el: HTMLFormElement;
5
+ $refs: {
6
+ headers: HTMLInputElement[];
7
+ };
5
8
  }
6
9
  /**
7
10
  * FrameForm class.
8
11
  */
9
- export declare class FrameForm<T extends BaseProps = BaseProps> extends Base<T & FrameFormProps> {
12
+ export declare class FrameForm<T extends BaseProps = BaseProps> extends AbstractFrameTrigger<T & FrameFormProps> {
10
13
  /**
11
14
  * Config.
12
15
  */
13
16
  static config: BaseConfig;
14
17
  /**
15
- * Get the form action.
18
+ * Form submission method.
16
19
  */
17
- get action(): string;
20
+ get method(): "post" | "get";
21
+ /**
22
+ * Add params to the requested URL for GET submissions.
23
+ */
24
+ get url(): URL;
25
+ /**
26
+ * Add body to the request for POST submissions.
27
+ */
28
+ get requestInit(): RequestInit;
29
+ /**
30
+ * Prevent submit on forms.
31
+ */
32
+ onSubmit({ event }: {
33
+ event: SubmitEvent;
34
+ target: FrameForm;
35
+ }): void;
18
36
  }