@wix/site-service-interactions 1.0.13 → 1.0.14
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/cjs/build/definition/index.js.map +1 -1
- package/cjs/build/implementations/InteractionsScriptElement.js.map +1 -1
- package/cjs/build/implementations/configEntries.js +4 -6
- package/cjs/build/implementations/configEntries.js.map +1 -1
- package/cjs/build/implementations/coreInteractions.js +1 -1
- package/cjs/build/implementations/coreInteractions.js.map +1 -1
- package/cjs/build/implementations/interactions.js +1 -1
- package/cjs/build/implementations/interactions.js.map +1 -1
- package/cjs/build/implementations/interactionsExternal.js +1 -1
- package/cjs/build/implementations/interactionsExternal.js.map +1 -1
- package/cjs/build/implementations/interactionsService.js.map +1 -1
- package/cjs/build/implementations/reviveScriptElement.js.map +1 -1
- package/cjs/build/implementations/v2/InteractionsScriptElement.js.map +1 -1
- package/cjs/build/implementations/v2/authoredStyles.js +6 -11
- package/cjs/build/implementations/v2/authoredStyles.js.map +1 -1
- package/cjs/build/implementations/v2/cdnUrls.js.map +1 -1
- package/cjs/build/implementations/v2/clickTriggerCss.js +1 -1
- package/cjs/build/implementations/v2/clickTriggerCss.js.map +1 -1
- package/cjs/build/implementations/v2/configCss.js.map +1 -1
- package/cjs/build/implementations/v2/configStyleHeadContent.js.map +1 -1
- package/cjs/build/implementations/v2/interactionsScriptContent.js +2 -2
- package/cjs/build/implementations/v2/interactionsScriptContent.js.map +1 -1
- package/cjs/build/implementations/v2/motionPresets.js +1 -1
- package/cjs/build/implementations/v2/motionPresets.js.map +1 -1
- package/cjs/package.json +1 -1
- package/definition/package.json +1 -1
- package/dist/definition/index.js.map +1 -1
- package/dist/implementations/InteractionsScriptElement.js.map +1 -1
- package/dist/implementations/configEntries.js +4 -6
- package/dist/implementations/configEntries.js.map +1 -1
- package/dist/implementations/coreInteractions.js +1 -1
- package/dist/implementations/coreInteractions.js.map +1 -1
- package/dist/implementations/interactions.js +1 -1
- package/dist/implementations/interactions.js.map +1 -1
- package/dist/implementations/interactionsExternal.js +1 -1
- package/dist/implementations/interactionsExternal.js.map +1 -1
- package/dist/implementations/interactionsService.js.map +1 -1
- package/dist/implementations/reviveScriptElement.js.map +1 -1
- package/dist/implementations/v2/InteractionsScriptElement.js.map +1 -1
- package/dist/implementations/v2/authoredStyles.js +6 -11
- package/dist/implementations/v2/authoredStyles.js.map +1 -1
- package/dist/implementations/v2/cdnUrls.js.map +1 -1
- package/dist/implementations/v2/clickTriggerCss.js +1 -1
- package/dist/implementations/v2/clickTriggerCss.js.map +1 -1
- package/dist/implementations/v2/configCss.js.map +1 -1
- package/dist/implementations/v2/configStyleHeadContent.js.map +1 -1
- package/dist/implementations/v2/interactionsScriptContent.js +2 -2
- package/dist/implementations/v2/interactionsScriptContent.js.map +1 -1
- package/dist/implementations/v2/motionPresets.js +1 -1
- package/dist/implementations/v2/motionPresets.js.map +1 -1
- package/implementations/package.json +1 -1
- package/package.json +22 -22
- package/src/definition/index.ts +11 -11
- package/src/implementations/InteractionsScriptElement.tsx +16 -16
- package/src/implementations/configEntries.ts +61 -63
- package/src/implementations/coreInteractions.ts +86 -86
- package/src/implementations/interactions.ts +8 -8
- package/src/implementations/interactionsExternal.ts +11 -11
- package/src/implementations/interactionsService.ts +8 -8
- package/src/implementations/reviveScriptElement.ts +8 -8
- package/src/implementations/v2/InteractionsScriptElement.tsx +21 -26
- package/src/implementations/v2/authoredStyles.ts +74 -79
- package/src/implementations/v2/cdnUrls.ts +1 -1
- package/src/implementations/v2/clickTriggerCss.ts +25 -25
- package/src/implementations/v2/configCss.ts +3 -3
- package/src/implementations/v2/configStyleHeadContent.ts +3 -3
- package/src/implementations/v2/interactionsScriptContent.ts +31 -31
- package/src/implementations/v2/motionPresets.ts +27 -27
- package/types/package.json +1 -1
|
@@ -19,117 +19,117 @@ import { getV2ConfigStyleHeadContent } from './v2/configStyleHeadContent'
|
|
|
19
19
|
import { getV2ModulePreloadHeadContent } from './v2/interactionsScriptContent'
|
|
20
20
|
import { getAuthoredStylesCss } from './v2/authoredStyles'
|
|
21
21
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
getInteractConfigEntries,
|
|
23
|
+
getInteractiveKeys,
|
|
24
|
+
getResolvedConfigEntries,
|
|
25
|
+
resolveInteractKeyConflicts,
|
|
26
26
|
} from './configEntries'
|
|
27
27
|
|
|
28
28
|
/** An entry without interactions contributes CSS only; nothing has to load Interact for it. */
|
|
29
29
|
const getRuntimeConfigs = (entries: InteractConfigEntry[]): InteractConfig[] =>
|
|
30
|
-
|
|
30
|
+
entries.map(({ interact }) => interact).filter(({ interactions }) => interactions.length > 0)
|
|
31
31
|
|
|
32
32
|
const initInteract = (entries: InteractConfigEntry[], isReducedMotion: boolean) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
// Bundled runtime is v1. v2 on the client is the CDN loader.
|
|
34
|
+
Interact.forceReducedMotion = isReducedMotion
|
|
35
|
+
Interact.setup({
|
|
36
|
+
scrollOptionsGetter: () => globalThis.document.getElementById('site-root'),
|
|
37
|
+
allowA11yTriggers: true,
|
|
38
|
+
viewEnter: {
|
|
39
|
+
useSafeViewEnter: true,
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
for (const { interact } of entries) {
|
|
43
|
+
Interact.create(interact as InteractV1Config)
|
|
44
|
+
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const getV2HeadContent = (entries: InteractConfigEntry[]): string => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
const generatedCss = entries
|
|
49
|
+
.map(({ interact }) => getV2ConfigCss(interact).trim())
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.join('\n')
|
|
52
|
+
const css = [getAuthoredStylesCss(entries).trim(), generatedCss].filter(Boolean).join('\n')
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
return (
|
|
55
|
+
(getRuntimeConfigs(entries).length ? getV2ModulePreloadHeadContent() : '') +
|
|
56
|
+
(css ? getV2ConfigStyleHeadContent(css) : '')
|
|
57
|
+
)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
const initRuntime = (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
entries: InteractConfigEntry[],
|
|
62
|
+
isReducedMotion: boolean,
|
|
63
|
+
getService: ServicesManager['getService'],
|
|
64
|
+
useV2: boolean
|
|
65
65
|
) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
const runtimeConfigs = getRuntimeConfigs(entries)
|
|
67
|
+
if (!runtimeConfigs.length) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
const pageId = getService(PagesDefinition).currentPageId()
|
|
72
|
+
if (!pageId) {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
const { addTopComponent } = getService(ProvideComponentServiceDefinition)
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
if (!useV2) {
|
|
79
|
+
// v1 serializes a single config and has no path for authored-style CSS, so anything beyond
|
|
80
|
+
// that would be silently dropped.
|
|
81
|
+
if (entries.length > 1 || entries.some(({ styles }) => styles?.length)) {
|
|
82
|
+
throw new Error('Multiple interactions config entries and authored styles require Interact v2.')
|
|
83
|
+
}
|
|
84
|
+
addTopComponent(createInteractionsScriptElement(runtimeConfigs[0], isReducedMotion, pageId))
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
addTopComponent(createV2InteractionsScriptElement(runtimeConfigs, isReducedMotion, pageId))
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
const getInteractiveComps = (entries: InteractConfigEntry[]): Set<string> => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
const keys = new Set<string>()
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
getInteractiveKeys(entry).forEach((key) => keys.add(key))
|
|
95
|
+
}
|
|
96
|
+
return keys
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
export const coreInteractions = (config: IInteractionsServiceConfig, getService: ServicesManager['getService']) => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
const { reducedMotion: isReducedMotion } = getService(DeviceInfoDefinition)
|
|
101
|
+
const { isOpen } = getService(ExperimentsDefinition)
|
|
102
|
+
const useV2 = isOpen('specs.thunderbolt.vsm_interactions_v2')
|
|
103
|
+
// The v1 runtime has no preset registration and doesn't understand LEGACY_* names; skip
|
|
104
|
+
// aliasing on those paths so named reaction effects remain resolvable.
|
|
105
|
+
const entries = resolveInteractKeyConflicts((useV2 ? getResolvedConfigEntries : getInteractConfigEntries)(config))
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
107
|
+
if (useV2) {
|
|
108
|
+
// Both belong in the head: the preload links only help while the document is still parsing,
|
|
109
|
+
// and the generated CSS hides elements before their entrance effect, so it has to apply
|
|
110
|
+
// before the body renders.
|
|
111
|
+
if (!process.env.browser) {
|
|
112
|
+
const headContent = getV2HeadContent(entries)
|
|
113
|
+
// GENERAL rather than setHead's AFTER_SECURITY default: thunderbolt only reads the
|
|
114
|
+
// after-security bucket on its SEO path, so the default drops this on every normal
|
|
115
|
+
// request. GENERAL reaches the document as raw markup before `</head>`.
|
|
116
|
+
if (headContent) {
|
|
117
|
+
getService(HeadAppenderDefinition).setHead(headContent, HeadContentType.GENERAL)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
initRuntime(entries, isReducedMotion, getService, true)
|
|
122
|
+
} else if (isOpen('specs.thunderbolt.interactionsInSsr')) {
|
|
123
|
+
// Emits the config and loader scripts. Works in both environments: SSR serves markup the
|
|
124
|
+
// parser executes, and on the client the loader's ref revives it into a script that runs —
|
|
125
|
+
// which is what pages rendered only on the client (password-protected / members-only,
|
|
126
|
+
// client-side navigation) need, since they never get a server-rendered document.
|
|
127
|
+
initRuntime(entries, isReducedMotion, getService, false)
|
|
128
|
+
} else {
|
|
129
|
+
initInteract(entries, isReducedMotion)
|
|
130
|
+
}
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
return {
|
|
133
|
+
interactiveComps: getInteractiveComps(entries),
|
|
134
|
+
}
|
|
135
135
|
}
|
|
@@ -3,16 +3,16 @@ import type { IInteractionsService, IInteractionsServiceConfig } from '../defini
|
|
|
3
3
|
import { coreInteractions } from './coreInteractions'
|
|
4
4
|
|
|
5
5
|
export const createInteractions = (
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
config: IInteractionsServiceConfig,
|
|
7
|
+
getService: ServicesManager['getService']
|
|
8
8
|
): IInteractionsService => {
|
|
9
|
-
|
|
9
|
+
const { interactiveComps } = coreInteractions(config, getService)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
const getConfigPath = (path: string) => path.replace(/\[[^\]]+]/g, '[]')
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
const hasInteractions: IInteractionsService['hasInteractions'] = (path) => interactiveComps.has(getConfigPath(path))
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
return {
|
|
16
|
+
hasInteractions,
|
|
17
|
+
}
|
|
18
18
|
}
|
|
@@ -5,21 +5,21 @@ import { coreInteractions } from './coreInteractions'
|
|
|
5
5
|
import { hasClickTriggerCss, wixInteractElementCss } from './stylesToAdd'
|
|
6
6
|
|
|
7
7
|
export const createInteractionsExternal = (
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
config: IInteractionsServiceConfig,
|
|
9
|
+
getService: ServicesManager['getService']
|
|
10
10
|
): IInteractionsService => {
|
|
11
|
-
|
|
11
|
+
const provideCssService = getService(ProvideCssDefinition)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const styles = [wixInteractElementCss, hasClickTriggerCss]
|
|
14
|
+
const cssToAdd = styles.join('\n')
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
provideCssService.addStyle(cssToAdd)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const { interactiveComps } = coreInteractions(config, getService)
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
const hasInteractions: IInteractionsService['hasInteractions'] = (path) => interactiveComps.has(path)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
return {
|
|
23
|
+
hasInteractions,
|
|
24
|
+
}
|
|
25
25
|
}
|
|
@@ -6,15 +6,15 @@ import { createInteractions } from './interactions'
|
|
|
6
6
|
import { createInteractionsExternal } from './interactionsExternal'
|
|
7
7
|
|
|
8
8
|
export const InteractionsService = implementStaticService(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
InteractionsDefinition,
|
|
10
|
+
({ config, getService }: StaticServiceFactoryOpts<IInteractionsServiceConfig>) => {
|
|
11
|
+
return createInteractions(config, getService)
|
|
12
|
+
}
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
export const InteractionsServiceExternal = implementStaticService(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
InteractionsDefinition,
|
|
17
|
+
({ config, getService }: StaticServiceFactoryOpts<IInteractionsServiceConfig>) => {
|
|
18
|
+
return createInteractionsExternal(config, getService)
|
|
19
|
+
}
|
|
20
20
|
)
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
* connected, and `connect()` returns early for them, so only the first instance serves a key.
|
|
17
17
|
*/
|
|
18
18
|
export const reviveScriptElement = (rendered: HTMLScriptElement | null) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
if (!rendered?.isConnected) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const executable = globalThis.document.createElement('script')
|
|
24
|
+
executable.type = rendered.type
|
|
25
|
+
executable.id = rendered.id
|
|
26
|
+
executable.textContent = rendered.textContent
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
rendered.replaceWith(executable)
|
|
29
29
|
}
|
|
@@ -11,32 +11,27 @@ const DANGEROUSLY_SET_INNER_HTML = ['dangerously', 'Set', 'Inner', 'HTML']
|
|
|
11
11
|
* element early. `\u003c` is valid JSON that `JSON.parse` reads back as `<`.
|
|
12
12
|
*/
|
|
13
13
|
const serializeConfigForScriptElement = (config: InteractConfig | InteractConfig[]): string =>
|
|
14
|
-
|
|
14
|
+
JSON.stringify(config).replace(/</g, '\\u003c')
|
|
15
15
|
|
|
16
16
|
export const createV2InteractionsScriptElement =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
(config: InteractConfig | InteractConfig[], isReducedMotion: boolean, pageId: string): FunctionComponent =>
|
|
18
|
+
() => {
|
|
19
|
+
// This ID is also interpolated into raw module-script text. Percent-encoding removes
|
|
20
|
+
// script delimiters and template-literal syntax while keeping the ID deterministic.
|
|
21
|
+
const safePageId = encodeURIComponent(pageId)
|
|
22
|
+
const configId = `interactions-config-${safePageId}`
|
|
23
|
+
const interactionsScriptContent = getV2InteractionsScriptContent(configId, isReducedMotion)
|
|
24
|
+
const configProps = {
|
|
25
|
+
[DANGEROUSLY_SET_INNER_HTML.join('')]: { __html: serializeConfigForScriptElement(config) },
|
|
26
|
+
}
|
|
27
|
+
const loaderProps = {
|
|
28
|
+
[DANGEROUSLY_SET_INNER_HTML.join('')]: { __html: interactionsScriptContent },
|
|
29
|
+
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{...loaderProps}
|
|
39
|
-
/>
|
|
40
|
-
</>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<script type="application/json" id={configId} {...configProps} />
|
|
34
|
+
<script type="module" id={`interactions-loader-${safePageId}`} ref={reviveScriptElement} {...loaderProps} />
|
|
35
|
+
</>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -3,106 +3,101 @@ import type { InteractConfigEntry, InteractStyleRule } from '../../definition'
|
|
|
3
3
|
const SELECTOR_TOKEN = /"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[()[\]]|,/g
|
|
4
4
|
|
|
5
5
|
const escapeCssString = (value: string): string =>
|
|
6
|
-
|
|
7
|
-
.replace(/\\/g, '\\\\')
|
|
8
|
-
.replace(/"/g, '\\"')
|
|
9
|
-
.replace(/\n/g, '\\a ')
|
|
10
|
-
.replace(/\r/g, '\\d ')
|
|
11
|
-
.replace(/\f/g, '\\c ')
|
|
6
|
+
value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\a ').replace(/\r/g, '\\d ').replace(/\f/g, '\\c ')
|
|
12
7
|
|
|
13
8
|
const splitSelectorList = (selector: string): string[] => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
const parts: string[] = []
|
|
10
|
+
let depth = 0
|
|
11
|
+
let start = 0
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
13
|
+
SELECTOR_TOKEN.lastIndex = 0
|
|
14
|
+
for (let match = SELECTOR_TOKEN.exec(selector); match; match = SELECTOR_TOKEN.exec(selector)) {
|
|
15
|
+
const token = match[0]
|
|
16
|
+
if (token === '(' || token === '[') {
|
|
17
|
+
depth++
|
|
18
|
+
} else if (token === ')' || token === ']') {
|
|
19
|
+
depth--
|
|
20
|
+
} else if (token === ',' && depth === 0) {
|
|
21
|
+
parts.push(selector.slice(start, match.index))
|
|
22
|
+
start = match.index + 1
|
|
23
|
+
}
|
|
24
|
+
}
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
parts.push(selector.slice(start))
|
|
27
|
+
return parts.map((part) => part.trim()).filter(Boolean)
|
|
33
28
|
}
|
|
34
29
|
|
|
35
30
|
const indentCssBlock = (css: string): string =>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
css
|
|
32
|
+
.split('\n')
|
|
33
|
+
.map((line) => (line ? ` ${line}` : line))
|
|
34
|
+
.join('\n')
|
|
40
35
|
|
|
41
36
|
const renderProperties = (properties: Record<string, string>): string =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
Object.entries(properties)
|
|
38
|
+
.map(([property, value]) => ` ${property}: ${value};`)
|
|
39
|
+
.join('\n')
|
|
45
40
|
|
|
46
41
|
const wrapWithConditions = (block: string, rule: InteractStyleRule): string => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
let result = block
|
|
43
|
+
if (rule['@container']) {
|
|
44
|
+
result = `@container ${rule['@container']} {\n${indentCssBlock(result)}\n}`
|
|
45
|
+
}
|
|
46
|
+
if (rule['@media']) {
|
|
47
|
+
result = `@media ${rule['@media']} {\n${indentCssBlock(result)}\n}`
|
|
48
|
+
}
|
|
49
|
+
if (rule['@supports']) {
|
|
50
|
+
result = `@supports ${rule['@supports']} {\n${indentCssBlock(result)}\n}`
|
|
51
|
+
}
|
|
52
|
+
return result
|
|
58
53
|
}
|
|
59
54
|
|
|
60
55
|
const buildRuleSelector = (namespace: string, rule: InteractStyleRule): string => {
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
const scopeWrapper = `[data-interact-key="${escapeCssString(namespace)}"]`
|
|
57
|
+
const scopeRoot = `${scopeWrapper} > :first-child`
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
59
|
+
if (!rule.key) {
|
|
60
|
+
if (!rule.selector) {
|
|
61
|
+
return scopeRoot
|
|
62
|
+
}
|
|
63
|
+
return splitSelectorList(rule.selector)
|
|
64
|
+
.map((selector) => `${scopeRoot} ${selector}`)
|
|
65
|
+
.join(', ')
|
|
66
|
+
}
|
|
72
67
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
const escapedKey = escapeCssString(rule.key)
|
|
69
|
+
const keyWrapper = rule.key === namespace ? scopeWrapper : `${scopeWrapper} [data-interact-key="${escapedKey}"]`
|
|
70
|
+
const keyRoot = `${keyWrapper} > :first-child`
|
|
71
|
+
if (!rule.selector) {
|
|
72
|
+
return keyRoot
|
|
73
|
+
}
|
|
79
74
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
const separator = /^\s/.test(rule.selector) ? ' ' : ''
|
|
76
|
+
return splitSelectorList(rule.selector)
|
|
77
|
+
.map((selector) => `${keyRoot}${separator}${selector}`)
|
|
78
|
+
.join(', ')
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
const getEntryStylesCss = ({ namespace, styles = [] }: InteractConfigEntry): string => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
if (!styles.length) {
|
|
83
|
+
return ''
|
|
84
|
+
}
|
|
85
|
+
if (!namespace) {
|
|
86
|
+
throw new Error('Interactions config entries with authored styles must define a namespace.')
|
|
87
|
+
}
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
89
|
+
return styles
|
|
90
|
+
.map((rule) => {
|
|
91
|
+
const properties = renderProperties(rule.properties)
|
|
92
|
+
if (!properties) {
|
|
93
|
+
return ''
|
|
94
|
+
}
|
|
95
|
+
const block = `${buildRuleSelector(namespace, rule)} {\n${properties}\n}`
|
|
96
|
+
return wrapWithConditions(block, rule)
|
|
97
|
+
})
|
|
98
|
+
.filter(Boolean)
|
|
99
|
+
.join('\n\n')
|
|
105
100
|
}
|
|
106
101
|
|
|
107
102
|
export const getAuthoredStylesCss = (entries: InteractConfigEntry[]): string =>
|
|
108
|
-
|
|
103
|
+
entries.map(getEntryStylesCss).filter(Boolean).join('\n\n')
|
|
@@ -3,4 +3,4 @@ export const INTERACT_V2_IMPORT_URL = 'https://cdn.jsdelivr.net/npm/@wix/interac
|
|
|
3
3
|
export const LEGACY_WIX_MOTION_PRESETS_IMPORT_URL = 'https://cdn.jsdelivr.net/npm/@wix/wix-motion-presets@1.2.0/+esm'
|
|
4
4
|
|
|
5
5
|
export const MOTION_PRESETS_IMPORT_URL =
|
|
6
|
-
|
|
6
|
+
'https://cdn.jsdelivr.net/npm/@wix/motion-presets@1.0.7/dist/browser/motion-presets.min.js'
|
|
@@ -7,38 +7,38 @@ const escapeCssAttr = (value: string): string => value.replace(/\\/g, '\\\\').re
|
|
|
7
7
|
* plus the `[data-interact-key="…"]` prefix from `CSSRuleToString`.
|
|
8
8
|
*/
|
|
9
9
|
const getInteractionSourceSelector = (
|
|
10
|
-
|
|
10
|
+
interaction: Pick<Interaction, 'key' | 'selector' | 'listContainer' | 'listItemSelector'>
|
|
11
11
|
): string => {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const { key, selector, listContainer, listItemSelector } = interaction
|
|
13
|
+
const attrSelector = `[data-interact-key="${escapeCssAttr(key)}"]`
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
if (listContainer) {
|
|
16
|
+
const itemFilter = listItemSelector ? ` > ${listItemSelector}` : ''
|
|
17
|
+
const inner = selector ? `${listContainer}${itemFilter} ${selector}` : `${listContainer}${itemFilter || ' > *'}`
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
return `${attrSelector} ${inner}`
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
if (selector) {
|
|
23
|
+
return `${attrSelector} ${selector}`
|
|
24
|
+
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
return `${attrSelector} > :first-child`
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/** Emits `cursor: pointer` for click interaction triggers (`'activate' || 'click'`), one rule per unique source selector. */
|
|
30
30
|
export const getClickTriggerCss = (config: InteractConfig): string => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
31
|
+
const selectors = [
|
|
32
|
+
...new Set(
|
|
33
|
+
config.interactions
|
|
34
|
+
.filter(({ trigger }) => trigger === 'activate' || trigger === 'click')
|
|
35
|
+
.map(getInteractionSourceSelector)
|
|
36
|
+
),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
if (!selectors.length) {
|
|
40
|
+
return ''
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return selectors.map((selector) => `${selector} {\ncursor: pointer;\n}`).join('\n')
|
|
44
44
|
}
|
|
@@ -11,8 +11,8 @@ Interact.registerEffects(motionPresets as unknown as PresetRegistry)
|
|
|
11
11
|
|
|
12
12
|
/** Emit animation FOUC CSS and click-trigger cursor rules. */
|
|
13
13
|
export const getV2ConfigCss = (config: InteractConfig): string => {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const animationCss = generate(config, true).trim()
|
|
15
|
+
const clickCss = getClickTriggerCss(config).trim()
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
return [animationCss, clickCss].filter(Boolean).join('\n')
|
|
18
18
|
}
|