@wix/site-service-interactions 1.0.12 → 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.d.ts +19 -3
- package/cjs/build/definition/index.js.map +1 -1
- package/cjs/build/implementations/InteractionsScriptElement.d.ts +1 -1
- package/cjs/build/implementations/InteractionsScriptElement.js.map +1 -1
- package/cjs/build/implementations/configEntries.d.ts +13 -0
- package/cjs/build/implementations/configEntries.js +78 -0
- package/cjs/build/implementations/configEntries.js.map +1 -0
- package/cjs/build/implementations/coreInteractions.d.ts +2 -2
- package/cjs/build/implementations/coreInteractions.js +50 -23
- 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.d.ts +5 -4
- package/cjs/build/implementations/interactionsService.js.map +1 -1
- package/cjs/build/implementations/reviveScriptElement.js.map +1 -1
- package/cjs/build/implementations/v2/InteractionsScriptElement.d.ts +2 -2
- package/cjs/build/implementations/v2/InteractionsScriptElement.js.map +1 -1
- package/cjs/build/implementations/v2/authoredStyles.d.ts +2 -0
- package/cjs/build/implementations/v2/authoredStyles.js +90 -0
- package/cjs/build/implementations/v2/authoredStyles.js.map +1 -0
- package/cjs/build/implementations/v2/cdnUrls.d.ts +2 -1
- package/cjs/build/implementations/v2/cdnUrls.js +3 -2
- package/cjs/build/implementations/v2/cdnUrls.js.map +1 -1
- package/cjs/build/implementations/v2/clickTriggerCss.d.ts +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.d.ts +1 -1
- package/cjs/build/implementations/v2/configCss.js +3 -36
- package/cjs/build/implementations/v2/configCss.js.map +1 -1
- package/cjs/build/implementations/v2/configStyleHeadContent.js +8 -6
- package/cjs/build/implementations/v2/configStyleHeadContent.js.map +1 -1
- package/cjs/build/implementations/v2/interactionsScriptContent.js +18 -7
- package/cjs/build/implementations/v2/interactionsScriptContent.js.map +1 -1
- package/cjs/build/implementations/v2/motionPresets.d.ts +14 -0
- package/cjs/build/implementations/v2/motionPresets.js +76 -0
- package/cjs/build/implementations/v2/motionPresets.js.map +1 -0
- package/cjs/build/service.d.ts +1 -1
- package/cjs/build/types/index.d.ts +1 -1
- package/cjs/package.json +1 -1
- package/definition/package.json +1 -1
- package/dist/definition/index.d.ts +19 -3
- package/dist/definition/index.js.map +1 -1
- package/dist/implementations/InteractionsScriptElement.d.ts +1 -1
- package/dist/implementations/InteractionsScriptElement.js.map +1 -1
- package/dist/implementations/configEntries.d.ts +13 -0
- package/dist/implementations/configEntries.js +71 -0
- package/dist/implementations/configEntries.js.map +1 -0
- package/dist/implementations/coreInteractions.d.ts +2 -2
- package/dist/implementations/coreInteractions.js +50 -23
- 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.d.ts +5 -4
- package/dist/implementations/interactionsService.js.map +1 -1
- package/dist/implementations/reviveScriptElement.js.map +1 -1
- package/dist/implementations/v2/InteractionsScriptElement.d.ts +2 -2
- package/dist/implementations/v2/InteractionsScriptElement.js.map +1 -1
- package/dist/implementations/v2/authoredStyles.d.ts +2 -0
- package/dist/implementations/v2/authoredStyles.js +86 -0
- package/dist/implementations/v2/authoredStyles.js.map +1 -0
- package/dist/implementations/v2/cdnUrls.d.ts +2 -1
- package/dist/implementations/v2/cdnUrls.js +2 -1
- package/dist/implementations/v2/cdnUrls.js.map +1 -1
- package/dist/implementations/v2/clickTriggerCss.d.ts +1 -1
- package/dist/implementations/v2/clickTriggerCss.js +1 -1
- package/dist/implementations/v2/clickTriggerCss.js.map +1 -1
- package/dist/implementations/v2/configCss.d.ts +1 -1
- package/dist/implementations/v2/configCss.js +2 -2
- package/dist/implementations/v2/configCss.js.map +1 -1
- package/dist/implementations/v2/configStyleHeadContent.js +8 -6
- package/dist/implementations/v2/configStyleHeadContent.js.map +1 -1
- package/dist/implementations/v2/interactionsScriptContent.js +19 -8
- package/dist/implementations/v2/interactionsScriptContent.js.map +1 -1
- package/dist/implementations/v2/motionPresets.d.ts +14 -0
- package/dist/implementations/v2/motionPresets.js +39 -0
- package/dist/implementations/v2/motionPresets.js.map +1 -0
- package/dist/service.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/implementations/package.json +1 -1
- package/package.json +24 -23
- package/src/definition/index.ts +21 -4
- package/src/implementations/InteractionsScriptElement.tsx +17 -17
- package/src/implementations/configEntries.ts +103 -0
- package/src/implementations/coreInteractions.ts +102 -68
- 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 +23 -28
- package/src/implementations/v2/authoredStyles.ts +103 -0
- package/src/implementations/v2/cdnUrls.ts +4 -1
- package/src/implementations/v2/clickTriggerCss.ts +26 -26
- package/src/implementations/v2/configCss.ts +6 -7
- package/src/implementations/v2/configStyleHeadContent.ts +9 -6
- package/src/implementations/v2/interactionsScriptContent.ts +36 -24
- package/src/implementations/v2/motionPresets.ts +60 -0
- package/src/types/index.ts +1 -1
- package/types/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Interact } from '@wix/interact'
|
|
2
|
-
import type { InteractConfig } from '@wix/interact'
|
|
2
|
+
import type { InteractConfig as InteractV1Config } from '@wix/interact'
|
|
3
3
|
import { DeviceInfoDefinition } from '@wix/site-service-device-info/definition'
|
|
4
4
|
import { ExperimentsDefinition } from '@wix/site-service-experiments/definition'
|
|
5
5
|
// Deliberately the deprecated pages service. Thunderbolt registers the site-service-pages page
|
|
@@ -11,91 +11,125 @@ import { HeadAppenderDefinition } from '@wix/site-service-head-appender/definiti
|
|
|
11
11
|
import { HeadContentType } from '@wix/site-service-head-appender/types'
|
|
12
12
|
import { ProvideComponentServiceDefinition } from '@wix/services-manager-react/core-services/provide-component'
|
|
13
13
|
import type { ServicesManager } from '@wix/services-manager/types'
|
|
14
|
+
import type { InteractConfig, InteractConfigEntry, IInteractionsServiceConfig } from '../definition'
|
|
14
15
|
import { createInteractionsScriptElement } from './InteractionsScriptElement'
|
|
15
16
|
import { createV2InteractionsScriptElement } from './v2/InteractionsScriptElement'
|
|
16
17
|
import { getV2ConfigCss } from './v2/configCss'
|
|
17
18
|
import { getV2ConfigStyleHeadContent } from './v2/configStyleHeadContent'
|
|
18
19
|
import { getV2ModulePreloadHeadContent } from './v2/interactionsScriptContent'
|
|
20
|
+
import { getAuthoredStylesCss } from './v2/authoredStyles'
|
|
21
|
+
import {
|
|
22
|
+
getInteractConfigEntries,
|
|
23
|
+
getInteractiveKeys,
|
|
24
|
+
getResolvedConfigEntries,
|
|
25
|
+
resolveInteractKeyConflicts,
|
|
26
|
+
} from './configEntries'
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
/** An entry without interactions contributes CSS only; nothing has to load Interact for it. */
|
|
29
|
+
const getRuntimeConfigs = (entries: InteractConfigEntry[]): InteractConfig[] =>
|
|
30
|
+
entries.map(({ interact }) => interact).filter(({ interactions }) => interactions.length > 0)
|
|
31
|
+
|
|
32
|
+
const initInteract = (entries: InteractConfigEntry[], isReducedMotion: boolean) => {
|
|
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
|
+
}
|
|
46
|
+
|
|
47
|
+
const getV2HeadContent = (entries: InteractConfigEntry[]): string => {
|
|
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
|
+
|
|
54
|
+
return (
|
|
55
|
+
(getRuntimeConfigs(entries).length ? getV2ModulePreloadHeadContent() : '') +
|
|
56
|
+
(css ? getV2ConfigStyleHeadContent(css) : '')
|
|
57
|
+
)
|
|
31
58
|
}
|
|
32
59
|
|
|
33
60
|
const initRuntime = (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
61
|
+
entries: InteractConfigEntry[],
|
|
62
|
+
isReducedMotion: boolean,
|
|
63
|
+
getService: ServicesManager['getService'],
|
|
64
|
+
useV2: boolean
|
|
38
65
|
) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
66
|
+
const runtimeConfigs = getRuntimeConfigs(entries)
|
|
67
|
+
if (!runtimeConfigs.length) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
42
70
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
71
|
+
const pageId = getService(PagesDefinition).currentPageId()
|
|
72
|
+
if (!pageId) {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
47
75
|
|
|
48
|
-
|
|
76
|
+
const { addTopComponent } = getService(ProvideComponentServiceDefinition)
|
|
49
77
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
+
}
|
|
53
87
|
|
|
54
|
-
|
|
88
|
+
addTopComponent(createV2InteractionsScriptElement(runtimeConfigs, isReducedMotion, pageId))
|
|
55
89
|
}
|
|
56
90
|
|
|
57
|
-
const getInteractiveComps = (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return acc
|
|
66
|
-
}, new Set<string>())
|
|
91
|
+
const getInteractiveComps = (entries: InteractConfigEntry[]): Set<string> => {
|
|
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
|
+
}
|
|
67
98
|
|
|
68
|
-
export const coreInteractions = (config:
|
|
69
|
-
|
|
70
|
-
|
|
99
|
+
export const coreInteractions = (config: IInteractionsServiceConfig, getService: ServicesManager['getService']) => {
|
|
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))
|
|
71
106
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
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
|
+
}
|
|
86
120
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
+
}
|
|
97
131
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
132
|
+
return {
|
|
133
|
+
interactiveComps: getInteractiveComps(entries),
|
|
134
|
+
}
|
|
101
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
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import type { FunctionComponent } from 'react'
|
|
3
|
-
import type { InteractConfig } from '@wix/interact'
|
|
3
|
+
import type { InteractConfig } from '@wix/interact-v2'
|
|
4
4
|
import { reviveScriptElement } from '../reviveScriptElement'
|
|
5
5
|
import { getV2InteractionsScriptContent } from './interactionsScriptContent'
|
|
6
6
|
|
|
@@ -10,33 +10,28 @@ const DANGEROUSLY_SET_INNER_HTML = ['dangerously', 'Set', 'Inner', 'HTML']
|
|
|
10
10
|
* The config is written as raw markup, so a `</script` inside any string value would close the
|
|
11
11
|
* element early. `\u003c` is valid JSON that `JSON.parse` reads back as `<`.
|
|
12
12
|
*/
|
|
13
|
-
const serializeConfigForScriptElement = (config: InteractConfig): string =>
|
|
14
|
-
|
|
13
|
+
const serializeConfigForScriptElement = (config: InteractConfig | InteractConfig[]): string =>
|
|
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
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { InteractConfigEntry, InteractStyleRule } from '../../definition'
|
|
2
|
+
|
|
3
|
+
const SELECTOR_TOKEN = /"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[()[\]]|,/g
|
|
4
|
+
|
|
5
|
+
const escapeCssString = (value: string): string =>
|
|
6
|
+
value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\a ').replace(/\r/g, '\\d ').replace(/\f/g, '\\c ')
|
|
7
|
+
|
|
8
|
+
const splitSelectorList = (selector: string): string[] => {
|
|
9
|
+
const parts: string[] = []
|
|
10
|
+
let depth = 0
|
|
11
|
+
let start = 0
|
|
12
|
+
|
|
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
|
+
}
|
|
25
|
+
|
|
26
|
+
parts.push(selector.slice(start))
|
|
27
|
+
return parts.map((part) => part.trim()).filter(Boolean)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const indentCssBlock = (css: string): string =>
|
|
31
|
+
css
|
|
32
|
+
.split('\n')
|
|
33
|
+
.map((line) => (line ? ` ${line}` : line))
|
|
34
|
+
.join('\n')
|
|
35
|
+
|
|
36
|
+
const renderProperties = (properties: Record<string, string>): string =>
|
|
37
|
+
Object.entries(properties)
|
|
38
|
+
.map(([property, value]) => ` ${property}: ${value};`)
|
|
39
|
+
.join('\n')
|
|
40
|
+
|
|
41
|
+
const wrapWithConditions = (block: string, rule: InteractStyleRule): string => {
|
|
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
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const buildRuleSelector = (namespace: string, rule: InteractStyleRule): string => {
|
|
56
|
+
const scopeWrapper = `[data-interact-key="${escapeCssString(namespace)}"]`
|
|
57
|
+
const scopeRoot = `${scopeWrapper} > :first-child`
|
|
58
|
+
|
|
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
|
+
}
|
|
67
|
+
|
|
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
|
+
}
|
|
74
|
+
|
|
75
|
+
const separator = /^\s/.test(rule.selector) ? ' ' : ''
|
|
76
|
+
return splitSelectorList(rule.selector)
|
|
77
|
+
.map((selector) => `${keyRoot}${separator}${selector}`)
|
|
78
|
+
.join(', ')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const getEntryStylesCss = ({ namespace, styles = [] }: InteractConfigEntry): string => {
|
|
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
|
+
}
|
|
88
|
+
|
|
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')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const getAuthoredStylesCss = (entries: InteractConfigEntry[]): string =>
|
|
103
|
+
entries.map(getEntryStylesCss).filter(Boolean).join('\n\n')
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export const INTERACT_V2_IMPORT_URL = 'https://cdn.jsdelivr.net/npm/@wix/interact@2.5.14/dist/es/web.min.js'
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const LEGACY_WIX_MOTION_PRESETS_IMPORT_URL = 'https://cdn.jsdelivr.net/npm/@wix/wix-motion-presets@1.2.0/+esm'
|
|
4
|
+
|
|
5
|
+
export const MOTION_PRESETS_IMPORT_URL =
|
|
6
|
+
'https://cdn.jsdelivr.net/npm/@wix/motion-presets@1.0.7/dist/browser/motion-presets.min.js'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InteractConfig, Interaction } from '@wix/interact'
|
|
1
|
+
import type { InteractConfig, Interaction } from '@wix/interact-v2'
|
|
2
2
|
|
|
3
3
|
const escapeCssAttr = (value: string): string => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
|
4
4
|
|
|
@@ -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
|
}
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { generate, Interact } from '@wix/interact-v2'
|
|
2
|
-
import type { InteractConfig } from '@wix/interact'
|
|
3
|
-
import type { InteractConfig as InteractV2Config } from '@wix/interact-v2'
|
|
4
|
-
import * as motionPresets from '@wix/wix-motion-presets'
|
|
2
|
+
import type { InteractConfig } from '@wix/interact-v2'
|
|
5
3
|
import { getClickTriggerCss } from './clickTriggerCss'
|
|
4
|
+
import { motionPresets } from './motionPresets'
|
|
6
5
|
|
|
7
6
|
type PresetRegistry = Parameters<typeof Interact.registerEffects>[0]
|
|
8
7
|
|
|
9
8
|
// Interact's registry is process-global. This module is only pulled into the server bundle
|
|
10
|
-
// (`process.env.browser` DCE), so
|
|
9
|
+
// (`process.env.browser` DCE), so both namespaces are registered once per Node process.
|
|
11
10
|
Interact.registerEffects(motionPresets as unknown as PresetRegistry)
|
|
12
11
|
|
|
13
12
|
/** Emit animation FOUC CSS and click-trigger cursor rules. */
|
|
14
13
|
export const getV2ConfigCss = (config: InteractConfig): string => {
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const animationCss = generate(config, true).trim()
|
|
15
|
+
const clickCss = getClickTriggerCss(config).trim()
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
return [animationCss, clickCss].filter(Boolean).join('\n')
|
|
19
18
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Head content is a raw HTML string
|
|
3
|
-
* style
|
|
4
|
-
*
|
|
5
|
-
* from the first `<` that could start a tag leaves valid CSS untouched.
|
|
2
|
+
* Head content is a raw HTML string. CSS values may contain tag-like text, but an actual closing
|
|
3
|
+
* style tag still terminates the raw-text element, and HeadAppender rejects `<script` substrings.
|
|
4
|
+
* Keep other CSS intact: drop content after a closing style tag and break script-like sequences.
|
|
6
5
|
*/
|
|
6
|
+
const STYLE_END_TAG = /<\/style(?=[\t\n\f\r />])/i
|
|
7
|
+
const SCRIPT_LIKE = /<script/gi
|
|
8
|
+
|
|
7
9
|
const sanitizeCssForStyleElement = (css: string): string => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
const styleEndTagIndex = css.search(STYLE_END_TAG)
|
|
11
|
+
const withoutStyleBreakout = styleEndTagIndex === -1 ? css : css.slice(0, styleEndTagIndex)
|
|
12
|
+
return withoutStyleBreakout.replace(SCRIPT_LIKE, '\\3c script')
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
export const getV2ConfigStyleHeadContent = (css: string): string => `<style>${sanitizeCssForStyleElement(css)}</style>`
|