@finsweet/webflow-apps-utils 1.0.5 → 1.0.7
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/stores/index.d.ts +1 -0
- package/dist/stores/index.js +1 -0
- package/dist/stores/isPreviewMode.d.ts +1 -0
- package/dist/stores/isPreviewMode.js +2 -0
- package/dist/types/dom.d.ts +1 -0
- package/dist/types/dom.js +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/webflow.d.ts +1 -1
- package/dist/ui/components/input/Input.svelte +18 -22
- package/dist/ui/components/layout/Layout.svelte +4 -2
- package/dist/ui/components/layout/Layout.svelte.d.ts +2 -0
- package/dist/ui/components/text/Text.svelte +4 -2
- package/dist/ui/components/text/types.d.ts +22 -0
- package/dist/ui/icons/ChevronIcon.svelte +1 -1
- package/dist/utils/constants.d.ts +5 -0
- package/dist/utils/constants.js +11 -0
- package/dist/utils/helpers/dom.d.ts +37 -0
- package/dist/utils/helpers/dom.js +104 -0
- package/dist/utils/helpers/encodeDecodeConfigs.d.ts +13 -0
- package/dist/utils/helpers/encodeDecodeConfigs.js +20 -0
- package/dist/utils/helpers/events.d.ts +19 -0
- package/dist/utils/helpers/events.js +28 -0
- package/dist/utils/helpers/forms.d.ts +22 -0
- package/dist/utils/helpers/forms.js +82 -0
- package/dist/utils/helpers/guards.d.ts +124 -0
- package/dist/utils/helpers/guards.js +107 -0
- package/dist/utils/helpers/index.d.ts +8 -0
- package/dist/utils/helpers/index.js +8 -0
- package/dist/utils/helpers/parseCSV.d.ts +6 -0
- package/dist/utils/helpers/parseCSV.js +29 -0
- package/dist/utils/helpers/string.d.ts +23 -0
- package/dist/utils/helpers/string.js +33 -0
- package/dist/utils/helpers/wait.d.ts +13 -0
- package/dist/utils/helpers/wait.js +27 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/webflow/CopyJSONButton.d.ts +54 -0
- package/dist/utils/webflow/CopyJSONButton.js +117 -0
- package/dist/utils/webflow/DisplayController.d.ts +55 -0
- package/dist/utils/webflow/DisplayController.js +91 -0
- package/dist/utils/webflow/Interaction.d.ts +47 -0
- package/dist/utils/webflow/Interaction.js +52 -0
- package/dist/utils/webflow/index.d.ts +4 -0
- package/dist/utils/webflow/index.js +4 -0
- package/dist/utils/webflow/webflow.d.ts +32 -0
- package/dist/utils/webflow/webflow.js +90 -0
- package/package.json +5 -6
package/dist/stores/index.d.ts
CHANGED
package/dist/stores/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isPreviewMode: import("svelte/store").Writable<boolean>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type FormField = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
package/dist/types/webflow.d.ts
CHANGED
|
@@ -474,28 +474,24 @@
|
|
|
474
474
|
</div>
|
|
475
475
|
{/snippet}
|
|
476
476
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
{
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
</Tooltip>
|
|
496
|
-
{:else}
|
|
497
|
-
{@render inputWrapper()}
|
|
498
|
-
{/if}
|
|
477
|
+
<Tooltip
|
|
478
|
+
message={hasAlert ? alert?.message || '' : ''}
|
|
479
|
+
placement="top"
|
|
480
|
+
listener="hover"
|
|
481
|
+
listenerout="hover"
|
|
482
|
+
showArrow={true}
|
|
483
|
+
hidden={!hasAlert}
|
|
484
|
+
disabled={!hasAlert || !alert?.message}
|
|
485
|
+
fontColor="var(--actionPrimaryText)"
|
|
486
|
+
width="max-content"
|
|
487
|
+
padding="6px"
|
|
488
|
+
bgColor={getTooltipColor(alert?.type || 'info')}
|
|
489
|
+
class="input-tooltip"
|
|
490
|
+
>
|
|
491
|
+
{#snippet target()}
|
|
492
|
+
{@render inputWrapper()}
|
|
493
|
+
{/snippet}
|
|
494
|
+
</Tooltip>
|
|
499
495
|
|
|
500
496
|
<style>
|
|
501
497
|
:global(.input-tooltip) {
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
containerMode?: boolean;
|
|
42
42
|
/** Size variant for the footer */
|
|
43
43
|
footerSize?: 'normal' | 'large';
|
|
44
|
+
/** Padding for the main content container (CSS value) */
|
|
45
|
+
mainContentPadding?: string;
|
|
44
46
|
/** Array of notification objects for tab status indicators */
|
|
45
47
|
notifications?: Array<{
|
|
46
48
|
/** Tab path this notification applies to */
|
|
@@ -76,6 +78,7 @@
|
|
|
76
78
|
sidebarWidth = '274px',
|
|
77
79
|
containerMode = false,
|
|
78
80
|
footerSize = 'normal',
|
|
81
|
+
mainContentPadding = 'var(--Spacing-24, 24px)',
|
|
79
82
|
notifications = [],
|
|
80
83
|
sidebar,
|
|
81
84
|
main,
|
|
@@ -240,7 +243,7 @@
|
|
|
240
243
|
<!-- Main Content -->
|
|
241
244
|
<div class="main-content" data-area="main">
|
|
242
245
|
{#if main}
|
|
243
|
-
<div class="main-content-container">
|
|
246
|
+
<div class="main-content-container" style="padding: {mainContentPadding}">
|
|
244
247
|
{#if showEditModeMessage}
|
|
245
248
|
<EditModeMessage />
|
|
246
249
|
{/if}
|
|
@@ -462,7 +465,6 @@
|
|
|
462
465
|
flex-direction: column;
|
|
463
466
|
align-items: flex-start;
|
|
464
467
|
align-self: stretch;
|
|
465
|
-
padding: var(--Spacing-24, 24px);
|
|
466
468
|
gap: var(--Spacing-16, 16px);
|
|
467
469
|
}
|
|
468
470
|
|
|
@@ -28,6 +28,8 @@ interface LayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
28
28
|
containerMode?: boolean;
|
|
29
29
|
/** Size variant for the footer */
|
|
30
30
|
footerSize?: 'normal' | 'large';
|
|
31
|
+
/** Padding for the main content container (CSS value) */
|
|
32
|
+
mainContentPadding?: string;
|
|
31
33
|
/** Array of notification objects for tab status indicators */
|
|
32
34
|
notifications?: Array<{
|
|
33
35
|
/** Tab path this notification applies to */
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|
// Link behavior
|
|
45
45
|
link = false,
|
|
46
|
+
linkHover = true,
|
|
46
47
|
|
|
47
48
|
// Event handlers
|
|
48
49
|
onclick,
|
|
@@ -211,6 +212,7 @@
|
|
|
211
212
|
const classes = ['labels'];
|
|
212
213
|
if (disabled) classes.push('disabled');
|
|
213
214
|
if (link) classes.push('link');
|
|
215
|
+
if (link && linkHover) classes.push('link-hover');
|
|
214
216
|
if (loading) classes.push('is-busy');
|
|
215
217
|
if (hasPopup && popupConfig.active) classes.push('active');
|
|
216
218
|
classes.push(className);
|
|
@@ -839,8 +841,8 @@
|
|
|
839
841
|
border-radius: 4px;
|
|
840
842
|
}
|
|
841
843
|
|
|
842
|
-
.labels.link:hover:not(.disabled),
|
|
843
|
-
.labels.link.is-busy {
|
|
844
|
+
.labels.link-hover:hover:not(.disabled),
|
|
845
|
+
.labels.link-hover.is-busy {
|
|
844
846
|
background-color: var(--defaultLightHover);
|
|
845
847
|
border-radius: 4px;
|
|
846
848
|
}
|
|
@@ -25,26 +25,48 @@ export interface PopupConfig {
|
|
|
25
25
|
onclick?: () => void;
|
|
26
26
|
}
|
|
27
27
|
export interface TextProps {
|
|
28
|
+
/** The text content to display */
|
|
28
29
|
label?: string;
|
|
30
|
+
/** Additional CSS classes to apply to the component */
|
|
29
31
|
class?: string;
|
|
32
|
+
/** Whether to render the label as HTML instead of plain text */
|
|
30
33
|
raw?: boolean;
|
|
34
|
+
/** Whether to capitalize the first letter of the text */
|
|
31
35
|
capitalize?: boolean;
|
|
36
|
+
/** Whether the component is disabled and non-interactive */
|
|
32
37
|
disabled?: boolean;
|
|
38
|
+
/** HTML title attribute for accessibility and hover tooltip */
|
|
33
39
|
title?: string;
|
|
40
|
+
/** Text wrapping behavior - 'nowrap' prevents wrapping, 'normal' allows wrapping */
|
|
34
41
|
wrap?: 'nowrap' | 'normal';
|
|
42
|
+
/** Horizontal alignment of the text content */
|
|
35
43
|
textAlign?: 'left' | 'center' | 'right';
|
|
44
|
+
/** Font size - use predefined sizes or custom CSS value */
|
|
36
45
|
fontSize?: TEXT_SIZES | string;
|
|
46
|
+
/** Font weight - use predefined weights or custom CSS value */
|
|
37
47
|
fontWeight?: TEXT_WEIGHTS | string;
|
|
48
|
+
/** Text color as any valid CSS color value */
|
|
38
49
|
fontColor?: string;
|
|
50
|
+
/** Fixed height for the component container */
|
|
39
51
|
height?: string;
|
|
52
|
+
/** Width at which text will be truncated with ellipsis */
|
|
40
53
|
ellipsisOnWidth?: string;
|
|
54
|
+
/** Tooltip configuration object with all tooltip-related settings */
|
|
41
55
|
tooltip?: Partial<TooltipProps>;
|
|
42
56
|
/** Specifies whether to show tooltip on the text or icon. Requires icon prop when set to 'icon' */
|
|
43
57
|
tooltipTarget?: 'text' | 'icon';
|
|
58
|
+
/** Configuration for action popup functionality (delete, reset, etc.) */
|
|
44
59
|
popup?: PopupConfig;
|
|
60
|
+
/** Icon component to display alongside the text */
|
|
45
61
|
icon?: Component | null;
|
|
62
|
+
/** Whether to show loading spinner instead of icon */
|
|
46
63
|
loading?: boolean;
|
|
64
|
+
/** Whether to style the text as a clickable link with pointer cursor */
|
|
47
65
|
link?: boolean;
|
|
66
|
+
/** Whether to show hover background effect when link is true (default: true) */
|
|
67
|
+
linkHover?: boolean;
|
|
68
|
+
/** Custom content snippet to render instead of the label text */
|
|
48
69
|
children?: Snippet;
|
|
70
|
+
/** Additional pill/badge content snippet to display */
|
|
49
71
|
pill?: Snippet;
|
|
50
72
|
}
|
|
@@ -59,3 +59,8 @@ export declare const BRAND: {
|
|
|
59
59
|
*/
|
|
60
60
|
export declare const FINSWEET_COMPONENTS_WEBFLOW_APP_ORIGINS: string[];
|
|
61
61
|
export declare const ACCEPTED_WEBFLOW_ELEMENTS_FOR_INSERTION: string[];
|
|
62
|
+
/**
|
|
63
|
+
* List of domains that are considered staging.
|
|
64
|
+
*/
|
|
65
|
+
export declare const STAGING_DOMAINS: string[];
|
|
66
|
+
export declare const FINSWEET_LOGO_URL = "https://cdn.prod.website-files.com/61819aaca0e7ac73f85a2d54/6865d68f194e84da9c6452e5_Components%20White%20Logo.svg";
|
package/dist/utils/constants.js
CHANGED
|
@@ -71,3 +71,14 @@ export const ACCEPTED_WEBFLOW_ELEMENTS_FOR_INSERTION = [
|
|
|
71
71
|
'Container',
|
|
72
72
|
'BlockContainer'
|
|
73
73
|
];
|
|
74
|
+
/**
|
|
75
|
+
* List of domains that are considered staging.
|
|
76
|
+
*/
|
|
77
|
+
export const STAGING_DOMAINS = [
|
|
78
|
+
'webflow.io',
|
|
79
|
+
'webflow-ext.com',
|
|
80
|
+
'localhost',
|
|
81
|
+
'canvas.webflow.com',
|
|
82
|
+
'server.wized.com'
|
|
83
|
+
];
|
|
84
|
+
export const FINSWEET_LOGO_URL = 'https://cdn.prod.website-files.com/61819aaca0e7ac73f85a2d54/6865d68f194e84da9c6452e5_Components%20White%20Logo.svg';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if an element is scrollable
|
|
3
|
+
* @param element
|
|
4
|
+
* @returns True or false
|
|
5
|
+
*/
|
|
6
|
+
export declare const isScrollable: (element: Element) => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Finds the first child text node of an element
|
|
9
|
+
* @param element The element to search into.
|
|
10
|
+
*/
|
|
11
|
+
export declare const findTextNode: (element: HTMLElement) => ChildNode | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Function to fetch the Document of a specified URL.
|
|
14
|
+
*
|
|
15
|
+
* @param url - The URL of the page.
|
|
16
|
+
* @param slug - [optional] The slug of the page.
|
|
17
|
+
* @returns
|
|
18
|
+
*/
|
|
19
|
+
export declare const fetchDocument: (url: string, slug?: string) => Promise<Document>;
|
|
20
|
+
/**
|
|
21
|
+
* Function to fetch the Elements of a specified URL.
|
|
22
|
+
*
|
|
23
|
+
* @param url - The URL of the page.
|
|
24
|
+
* @param tagName - The tag name of the elements to fetch. If not provided, returns the whole page as a string.
|
|
25
|
+
* @param asElement - [optional] If true, returns the element as an object.
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
export declare const fetchElements: (url: URL, tagName?: string, asElement?: boolean) => Promise<string[] | HTMLElement[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Fetch elements by attribute
|
|
31
|
+
*/
|
|
32
|
+
export declare const fetchElementsByAttribute: (url: URL, attribute: string) => Promise<string[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Checks if an element is visible
|
|
35
|
+
* @param element
|
|
36
|
+
*/
|
|
37
|
+
export declare const isVisible: (element: HTMLElement) => boolean;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { load } from 'cheerio';
|
|
2
|
+
import { FINSWEET_REVERSE_PROXY_URL } from '../constants';
|
|
3
|
+
import { isHTMLElement } from './guards';
|
|
4
|
+
/**
|
|
5
|
+
* Check if an element is scrollable
|
|
6
|
+
* @param element
|
|
7
|
+
* @returns True or false
|
|
8
|
+
*/
|
|
9
|
+
export const isScrollable = (element) => {
|
|
10
|
+
const { overflow } = getComputedStyle(element);
|
|
11
|
+
return overflow === 'auto' || overflow === 'scroll';
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Finds the first child text node of an element
|
|
15
|
+
* @param element The element to search into.
|
|
16
|
+
*/
|
|
17
|
+
export const findTextNode = (element) => {
|
|
18
|
+
let textNode;
|
|
19
|
+
for (const node of Array.from(element.childNodes)) {
|
|
20
|
+
if (isHTMLElement(node) && node.childNodes.length)
|
|
21
|
+
textNode = findTextNode(node);
|
|
22
|
+
else if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim())
|
|
23
|
+
textNode = node;
|
|
24
|
+
if (textNode)
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
return textNode;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Function to fetch the Document of a specified URL.
|
|
31
|
+
*
|
|
32
|
+
* @param url - The URL of the page.
|
|
33
|
+
* @param slug - [optional] The slug of the page.
|
|
34
|
+
* @returns
|
|
35
|
+
*/
|
|
36
|
+
export const fetchDocument = async (url, slug) => {
|
|
37
|
+
const pageUrl = new URL(url);
|
|
38
|
+
if (slug)
|
|
39
|
+
pageUrl.pathname = slug;
|
|
40
|
+
const target = `${FINSWEET_REVERSE_PROXY_URL}${pageUrl}`;
|
|
41
|
+
const response = await fetch(target);
|
|
42
|
+
const pageContent = await response.text();
|
|
43
|
+
const parser = new DOMParser();
|
|
44
|
+
return parser.parseFromString(pageContent, 'text/html');
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Function to fetch the Elements of a specified URL.
|
|
48
|
+
*
|
|
49
|
+
* @param url - The URL of the page.
|
|
50
|
+
* @param tagName - The tag name of the elements to fetch. If not provided, returns the whole page as a string.
|
|
51
|
+
* @param asElement - [optional] If true, returns the element as an object.
|
|
52
|
+
* @returns
|
|
53
|
+
*/
|
|
54
|
+
export const fetchElements = async (url, tagName, asElement) => {
|
|
55
|
+
const target = `${FINSWEET_REVERSE_PROXY_URL}${url.href}`;
|
|
56
|
+
const response = await fetch(target);
|
|
57
|
+
const html = await response.text();
|
|
58
|
+
const $ = load(html);
|
|
59
|
+
// return the whole page if no tag name is provided
|
|
60
|
+
if (!tagName)
|
|
61
|
+
return [$.html()];
|
|
62
|
+
// crawl page and get elements
|
|
63
|
+
if (asElement && tagName) {
|
|
64
|
+
const elements = $(tagName)
|
|
65
|
+
.map((_, el) => {
|
|
66
|
+
const outerHTML = $.html(el); // Get the outer HTML of each element
|
|
67
|
+
const parser = new DOMParser();
|
|
68
|
+
const parsedDoc = parser.parseFromString(outerHTML, 'text/html');
|
|
69
|
+
const element = parsedDoc.querySelector(tagName);
|
|
70
|
+
return element;
|
|
71
|
+
})
|
|
72
|
+
.get();
|
|
73
|
+
return elements;
|
|
74
|
+
}
|
|
75
|
+
// crawl page and get elements as strings
|
|
76
|
+
const elements = $(tagName)
|
|
77
|
+
.map((_, el) => {
|
|
78
|
+
const outerHTML = $.html(el);
|
|
79
|
+
return outerHTML;
|
|
80
|
+
})
|
|
81
|
+
.get();
|
|
82
|
+
return elements;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Fetch elements by attribute
|
|
86
|
+
*/
|
|
87
|
+
export const fetchElementsByAttribute = async (url, attribute) => {
|
|
88
|
+
const target = `${FINSWEET_REVERSE_PROXY_URL}${url.href}`;
|
|
89
|
+
const response = await fetch(target);
|
|
90
|
+
const html = await response.text();
|
|
91
|
+
const $ = load(html);
|
|
92
|
+
const elements = $(attribute)
|
|
93
|
+
.map((_, el) => {
|
|
94
|
+
const outerHTML = $.html(el);
|
|
95
|
+
return outerHTML;
|
|
96
|
+
})
|
|
97
|
+
.get();
|
|
98
|
+
return elements;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Checks if an element is visible
|
|
102
|
+
* @param element
|
|
103
|
+
*/
|
|
104
|
+
export const isVisible = (element) => !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a configuration object to a base64-encoded string.
|
|
3
|
+
* @param {object} configs
|
|
4
|
+
* @returns
|
|
5
|
+
*/
|
|
6
|
+
export declare const encodeComponentConfigs: (configs: object) => string;
|
|
7
|
+
/**
|
|
8
|
+
* Decodes a base64-encoded configuration string to a configuration object.
|
|
9
|
+
* @param configsString
|
|
10
|
+
* @param component
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
export declare const decodeComponentConfigs: <T>(configsString: string) => T;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a configuration object to a base64-encoded string.
|
|
3
|
+
* @param {object} configs
|
|
4
|
+
* @returns
|
|
5
|
+
*/
|
|
6
|
+
export const encodeComponentConfigs = (configs) => {
|
|
7
|
+
const previewModeConfigs = btoa(JSON.stringify(configs));
|
|
8
|
+
return previewModeConfigs;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Decodes a base64-encoded configuration string to a configuration object.
|
|
12
|
+
* @param configsString
|
|
13
|
+
* @param component
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
export const decodeComponentConfigs = (configsString) => {
|
|
17
|
+
const jsonString = atob(configsString);
|
|
18
|
+
const configParsed = JSON.parse(jsonString);
|
|
19
|
+
return configParsed;
|
|
20
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds an event listener to an element.
|
|
3
|
+
* @returns A callback to remove the event listener from the element.
|
|
4
|
+
*
|
|
5
|
+
* @param target
|
|
6
|
+
* @param type
|
|
7
|
+
* @param listener
|
|
8
|
+
* @param options
|
|
9
|
+
*/
|
|
10
|
+
export declare function addListener<TargetInterface extends EventTarget, Type extends TargetInterface extends Window ? keyof WindowEventMap | string : TargetInterface extends Document ? keyof DocumentEventMap | string : TargetInterface extends HTMLElement ? keyof HTMLElementEventMap | string : keyof ElementEventMap | string, Listener extends Type extends keyof WindowEventMap ? (this: Document, ev: WindowEventMap[Type]) => unknown : Type extends keyof DocumentEventMap ? (this: Document, ev: DocumentEventMap[Type]) => unknown : Type extends keyof HTMLElementEventMap ? (this: HTMLElement, ev: HTMLElementEventMap[Type]) => unknown : Type extends keyof ElementEventMap ? (this: Element, ev: ElementEventMap[Type]) => unknown : EventListenerOrEventListenerObject>(target: TargetInterface | null | undefined, type: Type, listener: Listener, options?: boolean | AddEventListenerOptions): () => void;
|
|
11
|
+
type AllowedEvent = keyof DocumentEventMap | 'w-close';
|
|
12
|
+
/**
|
|
13
|
+
* Dispatches a custom event that bubbles from the target.
|
|
14
|
+
* @param target The element where the event will originate.
|
|
15
|
+
* @param events The event name or an array of event names.
|
|
16
|
+
* @returns True if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise.
|
|
17
|
+
*/
|
|
18
|
+
export declare const simulateEvent: (target: EventTarget, events: AllowedEvent | Array<AllowedEvent>) => boolean;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { noop } from './noop';
|
|
2
|
+
/**
|
|
3
|
+
* Adds an event listener to an element.
|
|
4
|
+
* @returns A callback to remove the event listener from the element.
|
|
5
|
+
*
|
|
6
|
+
* @param target
|
|
7
|
+
* @param type
|
|
8
|
+
* @param listener
|
|
9
|
+
* @param options
|
|
10
|
+
*/
|
|
11
|
+
export function addListener(target, type, listener, options) {
|
|
12
|
+
if (!target)
|
|
13
|
+
return noop;
|
|
14
|
+
target.addEventListener(type, listener, options);
|
|
15
|
+
return () => target.removeEventListener(type, listener, options);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Dispatches a custom event that bubbles from the target.
|
|
19
|
+
* @param target The element where the event will originate.
|
|
20
|
+
* @param events The event name or an array of event names.
|
|
21
|
+
* @returns True if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise.
|
|
22
|
+
*/
|
|
23
|
+
export const simulateEvent = (target, events) => {
|
|
24
|
+
if (!Array.isArray(events))
|
|
25
|
+
events = [events];
|
|
26
|
+
const eventsSuccess = events.map((event) => target.dispatchEvent(new Event(event, { bubbles: true })));
|
|
27
|
+
return eventsSuccess.every((success) => success);
|
|
28
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FormField } from '../../types';
|
|
2
|
+
import { simulateEvent } from './events';
|
|
3
|
+
/**
|
|
4
|
+
* Clears the form field's value and emits an input and changed event.
|
|
5
|
+
* If the field is a checkbox or a radio, it will unselect it.
|
|
6
|
+
* @param field The `FormField` to clear.
|
|
7
|
+
* @param omitEvents By default, events are dispatched from the `FormField`. In some cases, these events might collide with other logic of the system.
|
|
8
|
+
* You can omit certain events from being dispatched by passing them in an array.
|
|
9
|
+
*/
|
|
10
|
+
export declare const clearFormField: (field: FormField, omitEvents?: Parameters<typeof simulateEvent>["1"]) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Gets the value of a given input element.
|
|
13
|
+
* @param {FormField} input
|
|
14
|
+
*/
|
|
15
|
+
export declare const getFormFieldValue: (input: FormField) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Sets a value to a FormField element and emits `click`, `input` and `change` Events.
|
|
18
|
+
*
|
|
19
|
+
* @param element The FormField to update.
|
|
20
|
+
* @param value `boolean` for Checkboxes and Radios, `string` for the rest.
|
|
21
|
+
*/
|
|
22
|
+
export declare const setFormFieldValue: (element: FormField, value: string | boolean) => void;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { FORM_CSS_CLASSES } from '../webflow';
|
|
2
|
+
import { simulateEvent } from './events';
|
|
3
|
+
import { isHTMLInputElement } from './guards';
|
|
4
|
+
/**
|
|
5
|
+
* Clears the form field's value and emits an input and changed event.
|
|
6
|
+
* If the field is a checkbox or a radio, it will unselect it.
|
|
7
|
+
* @param field The `FormField` to clear.
|
|
8
|
+
* @param omitEvents By default, events are dispatched from the `FormField`. In some cases, these events might collide with other logic of the system.
|
|
9
|
+
* You can omit certain events from being dispatched by passing them in an array.
|
|
10
|
+
*/
|
|
11
|
+
export const clearFormField = (field, omitEvents = []) => {
|
|
12
|
+
const { type } = field;
|
|
13
|
+
if (isHTMLInputElement(field) && ['checkbox', 'radio'].includes(type)) {
|
|
14
|
+
if (!field.checked)
|
|
15
|
+
return;
|
|
16
|
+
// Reset the field's value
|
|
17
|
+
field.checked = false;
|
|
18
|
+
// Emit DOM events
|
|
19
|
+
simulateEvent(field, ['click', 'input', 'change'].filter((event) => !omitEvents.includes(event)));
|
|
20
|
+
if (type === 'checkbox')
|
|
21
|
+
return;
|
|
22
|
+
// Clear custom radio button classes
|
|
23
|
+
const { parentElement } = field;
|
|
24
|
+
if (!parentElement)
|
|
25
|
+
return;
|
|
26
|
+
const radioInput = parentElement.querySelector(`.${FORM_CSS_CLASSES.radioInput}`);
|
|
27
|
+
if (!radioInput)
|
|
28
|
+
return;
|
|
29
|
+
radioInput.classList.remove(FORM_CSS_CLASSES.checkboxOrRadioFocus, FORM_CSS_CLASSES.checkboxOrRadioChecked);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Reset the field's value
|
|
33
|
+
field.value = '';
|
|
34
|
+
// Emit DOM events
|
|
35
|
+
simulateEvent(field, ['input', 'change'].filter((eventKey) => !omitEvents.includes(eventKey)));
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Gets the value of a given input element.
|
|
39
|
+
* @param {FormField} input
|
|
40
|
+
*/
|
|
41
|
+
export const getFormFieldValue = (input) => {
|
|
42
|
+
let { value } = input;
|
|
43
|
+
// Perform actions depending on input type
|
|
44
|
+
if (input.type === 'checkbox')
|
|
45
|
+
value = input.checked.toString();
|
|
46
|
+
if (input.type === 'radio') {
|
|
47
|
+
// Get the checked radio
|
|
48
|
+
const checkedOption = input
|
|
49
|
+
.closest('form')
|
|
50
|
+
?.querySelector(`input[name="${input.name}"]:checked`);
|
|
51
|
+
// If exists, set its value
|
|
52
|
+
value = isHTMLInputElement(checkedOption) ? checkedOption.value : '';
|
|
53
|
+
}
|
|
54
|
+
return value.toString();
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Sets a value to a FormField element and emits `click`, `input` and `change` Events.
|
|
58
|
+
*
|
|
59
|
+
* @param element The FormField to update.
|
|
60
|
+
* @param value `boolean` for Checkboxes and Radios, `string` for the rest.
|
|
61
|
+
*/
|
|
62
|
+
export const setFormFieldValue = (element, value) => {
|
|
63
|
+
const { type } = element;
|
|
64
|
+
const isRadio = type === 'radio';
|
|
65
|
+
const isCheckbox = type === 'checkbox';
|
|
66
|
+
if (isRadio || isCheckbox) {
|
|
67
|
+
if (!isHTMLInputElement(element) ||
|
|
68
|
+
typeof value !== 'boolean' ||
|
|
69
|
+
value === element.checked ||
|
|
70
|
+
(isRadio && value === false)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
element.checked = value;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
if (element.value === value)
|
|
77
|
+
return;
|
|
78
|
+
element.value = value.toString();
|
|
79
|
+
}
|
|
80
|
+
// Emit DOM events
|
|
81
|
+
simulateEvent(element, ['click', 'input', 'change']);
|
|
82
|
+
};
|