@syntrologie/adapt-nav 2.4.1 → 2.5.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.
- package/dist/NavWidget.d.ts +3 -3
- package/dist/NavWidget.d.ts.map +1 -1
- package/dist/NavWidget.js +47 -81
- package/dist/cdn.d.ts.map +1 -1
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +50 -20
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +5 -4
- package/dist/schema.d.ts +554 -95
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +6 -8
- package/dist/summarize.d.ts +2 -2
- package/dist/summarize.d.ts.map +1 -1
- package/dist/summarize.js +5 -5
- package/dist/types.d.ts +7 -49
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/node_modules/@syntrologie/sdk-contracts/dist/index.d.ts +105 -2
- package/node_modules/@syntrologie/sdk-contracts/dist/index.js +5 -3
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.d.ts +798 -1
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.js +21 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.d.ts +2 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.js +224 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ConditionStatusLine.test.js +102 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DetectionBadge.test.js +58 -6
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DismissedSection.test.js +18 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorCard.test.js +61 -2
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorPanelShell.test.js +478 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ElementHighlight.test.js +54 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.d.ts +2 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.js +257 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.d.ts +2 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.js +1015 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.js +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts +4 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.js +2 -2
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts +2 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.js +20 -3
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts +10 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.js +350 -87
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.js +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts +3 -3
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.js +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.js +5 -2
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts +24 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/{useShowWhenStatus.js → useTriggerWhenStatus.js} +18 -15
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts +3 -3
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.js +1 -1
- package/package.json +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useShowWhenStatus.d.ts +0 -24
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useShowWhenStatus.d.ts.map +0 -1
package/dist/NavWidget.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Adaptive Nav - NavWidget Component
|
|
3
3
|
*
|
|
4
4
|
* React component that renders a collapsible navigation tips accordion
|
|
5
|
-
* with per-item conditional visibility based on
|
|
5
|
+
* with per-item conditional visibility based on triggerWhen decision strategies.
|
|
6
6
|
*
|
|
7
7
|
* Demonstrates the compositional action pattern where child actions
|
|
8
8
|
* (nav:tip) serve as configuration data for the parent widget.
|
|
@@ -13,8 +13,8 @@ import type { NavConfig, NavWidgetProps, NavWidgetRuntime } from './types';
|
|
|
13
13
|
*
|
|
14
14
|
* This component demonstrates the compositional action pattern:
|
|
15
15
|
* - Parent (NavWidget) receives `config.actions` array
|
|
16
|
-
* - Each action has optional `
|
|
17
|
-
* - Parent evaluates
|
|
16
|
+
* - Each action has optional `triggerWhen` for per-item visibility
|
|
17
|
+
* - Parent evaluates triggerWhen and filters visible tips
|
|
18
18
|
* - Parent manages expand state and re-rendering on context changes
|
|
19
19
|
*/
|
|
20
20
|
export declare function NavWidget({ config, runtime, instanceId }: NavWidgetProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/NavWidget.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavWidget.d.ts","sourceRoot":"","sources":["../src/NavWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAgB,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"NavWidget.d.ts","sourceRoot":"","sources":["../src/NavWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAgB,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA8PzF;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,cAAc,2CAmMxE;AAMD;;GAEG;AACH,eAAO,MAAM,kBAAkB;qBAEhB,WAAW,WACb,SAAS,GAAG;QAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;CAiD3E,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
package/dist/NavWidget.js
CHANGED
|
@@ -3,12 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
* Adaptive Nav - NavWidget Component
|
|
4
4
|
*
|
|
5
5
|
* React component that renders a collapsible navigation tips accordion
|
|
6
|
-
* with per-item conditional visibility based on
|
|
6
|
+
* with per-item conditional visibility based on triggerWhen decision strategies.
|
|
7
7
|
*
|
|
8
8
|
* Demonstrates the compositional action pattern where child actions
|
|
9
9
|
* (nav:tip) serve as configuration data for the parent widget.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { purple, slateGrey } from '@syntro/design-system/tokens';
|
|
12
12
|
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
|
13
13
|
import { createRoot } from 'react-dom/client';
|
|
14
14
|
// ============================================================================
|
|
@@ -27,18 +27,17 @@ function escapeHtml(str) {
|
|
|
27
27
|
// ============================================================================
|
|
28
28
|
const baseStyles = {
|
|
29
29
|
container: {
|
|
30
|
-
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
31
|
-
padding: '8px',
|
|
30
|
+
fontFamily: 'var(--sc-font-family, system-ui, -apple-system, sans-serif)',
|
|
32
31
|
maxWidth: '100%',
|
|
33
32
|
overflow: 'hidden',
|
|
34
33
|
},
|
|
35
34
|
accordion: {
|
|
36
35
|
display: 'flex',
|
|
37
36
|
flexDirection: 'column',
|
|
38
|
-
gap: '
|
|
37
|
+
gap: 'var(--sc-content-item-gap, 6px)',
|
|
39
38
|
},
|
|
40
39
|
item: {
|
|
41
|
-
borderRadius: '8px',
|
|
40
|
+
borderRadius: 'var(--sc-content-border-radius, 8px)',
|
|
42
41
|
overflow: 'hidden',
|
|
43
42
|
transition: 'box-shadow 0.2s ease',
|
|
44
43
|
},
|
|
@@ -47,20 +46,21 @@ const baseStyles = {
|
|
|
47
46
|
alignItems: 'center',
|
|
48
47
|
gap: '8px',
|
|
49
48
|
width: '100%',
|
|
50
|
-
padding: '12px 16px',
|
|
49
|
+
padding: 'var(--sc-content-item-padding, 12px 16px)',
|
|
51
50
|
border: 'none',
|
|
52
51
|
cursor: 'pointer',
|
|
53
|
-
fontSize: '
|
|
52
|
+
fontSize: 'var(--sc-content-item-font-size, 15px)',
|
|
54
53
|
fontWeight: 500,
|
|
55
54
|
fontFamily: 'inherit',
|
|
56
55
|
textAlign: 'left',
|
|
57
56
|
transition: 'background-color 0.15s ease',
|
|
58
57
|
},
|
|
59
58
|
chevron: {
|
|
60
|
-
fontSize: '
|
|
59
|
+
fontSize: '20px',
|
|
61
60
|
transition: 'transform 0.2s ease',
|
|
62
61
|
marginLeft: 'auto',
|
|
63
62
|
flexShrink: 0,
|
|
63
|
+
color: 'var(--sc-content-chevron-color, currentColor)',
|
|
64
64
|
},
|
|
65
65
|
icon: {
|
|
66
66
|
fontSize: '16px',
|
|
@@ -69,10 +69,10 @@ const baseStyles = {
|
|
|
69
69
|
body: {
|
|
70
70
|
overflow: 'hidden',
|
|
71
71
|
transition: 'max-height 0.25s ease, padding-bottom 0.25s ease',
|
|
72
|
-
padding: '0 16px',
|
|
72
|
+
padding: 'var(--sc-content-body-padding, 0 16px 12px 16px)',
|
|
73
73
|
},
|
|
74
74
|
description: {
|
|
75
|
-
fontSize: '
|
|
75
|
+
fontSize: 'var(--sc-content-body-font-size, 14px)',
|
|
76
76
|
lineHeight: '1.5',
|
|
77
77
|
margin: 0,
|
|
78
78
|
},
|
|
@@ -91,11 +91,11 @@ const baseStyles = {
|
|
|
91
91
|
transition: 'background-color 0.15s ease',
|
|
92
92
|
},
|
|
93
93
|
categoryHeader: {
|
|
94
|
-
fontSize: '
|
|
94
|
+
fontSize: 'var(--sc-content-category-font-size, 12px)',
|
|
95
95
|
fontWeight: 600,
|
|
96
96
|
textTransform: 'uppercase',
|
|
97
97
|
letterSpacing: '0.05em',
|
|
98
|
-
padding: '
|
|
98
|
+
padding: 'var(--sc-content-category-padding, 8px 4px 4px 4px)',
|
|
99
99
|
},
|
|
100
100
|
emptyState: {
|
|
101
101
|
fontSize: '13px',
|
|
@@ -106,29 +106,29 @@ const baseStyles = {
|
|
|
106
106
|
const themeStyles = {
|
|
107
107
|
light: {
|
|
108
108
|
container: {
|
|
109
|
-
backgroundColor:
|
|
110
|
-
color:
|
|
109
|
+
backgroundColor: 'transparent',
|
|
110
|
+
color: 'inherit',
|
|
111
111
|
},
|
|
112
112
|
item: {
|
|
113
|
-
backgroundColor:
|
|
114
|
-
border:
|
|
113
|
+
backgroundColor: 'var(--sc-content-background)',
|
|
114
|
+
border: 'var(--sc-content-border)',
|
|
115
115
|
},
|
|
116
116
|
itemExpanded: {
|
|
117
117
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',
|
|
118
118
|
},
|
|
119
119
|
header: {
|
|
120
120
|
backgroundColor: 'transparent',
|
|
121
|
-
color:
|
|
121
|
+
color: 'var(--sc-content-text-color)',
|
|
122
122
|
},
|
|
123
123
|
headerHover: {
|
|
124
|
-
backgroundColor:
|
|
124
|
+
backgroundColor: 'var(--sc-content-background-hover)',
|
|
125
125
|
},
|
|
126
126
|
body: {
|
|
127
|
-
color:
|
|
127
|
+
color: 'var(--sc-content-text-secondary-color)',
|
|
128
128
|
},
|
|
129
129
|
linkButton: {
|
|
130
|
-
backgroundColor:
|
|
131
|
-
color:
|
|
130
|
+
backgroundColor: 'var(--sc-color-primary, #6366f1)',
|
|
131
|
+
color: '#ffffff',
|
|
132
132
|
},
|
|
133
133
|
categoryHeader: {
|
|
134
134
|
color: slateGrey[7],
|
|
@@ -139,29 +139,29 @@ const themeStyles = {
|
|
|
139
139
|
},
|
|
140
140
|
dark: {
|
|
141
141
|
container: {
|
|
142
|
-
backgroundColor:
|
|
143
|
-
color:
|
|
142
|
+
backgroundColor: 'transparent',
|
|
143
|
+
color: 'inherit',
|
|
144
144
|
},
|
|
145
145
|
item: {
|
|
146
|
-
backgroundColor:
|
|
147
|
-
border:
|
|
146
|
+
backgroundColor: 'var(--sc-content-background)',
|
|
147
|
+
border: 'var(--sc-content-border)',
|
|
148
148
|
},
|
|
149
149
|
itemExpanded: {
|
|
150
|
-
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.
|
|
150
|
+
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
151
151
|
},
|
|
152
152
|
header: {
|
|
153
153
|
backgroundColor: 'transparent',
|
|
154
|
-
color:
|
|
154
|
+
color: 'var(--sc-content-text-color)',
|
|
155
155
|
},
|
|
156
156
|
headerHover: {
|
|
157
|
-
backgroundColor:
|
|
157
|
+
backgroundColor: 'var(--sc-content-background-hover)',
|
|
158
158
|
},
|
|
159
159
|
body: {
|
|
160
|
-
color:
|
|
160
|
+
color: 'var(--sc-content-text-secondary-color)',
|
|
161
161
|
},
|
|
162
162
|
linkButton: {
|
|
163
|
-
backgroundColor:
|
|
164
|
-
color:
|
|
163
|
+
backgroundColor: 'var(--sc-color-primary, #6366f1)',
|
|
164
|
+
color: '#ffffff',
|
|
165
165
|
},
|
|
166
166
|
categoryHeader: {
|
|
167
167
|
color: slateGrey[8],
|
|
@@ -171,7 +171,7 @@ const themeStyles = {
|
|
|
171
171
|
},
|
|
172
172
|
},
|
|
173
173
|
};
|
|
174
|
-
function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
|
|
174
|
+
function NavTipItem({ item, isExpanded, isLast, onToggle, onNavigate, theme }) {
|
|
175
175
|
const [isHovered, setIsHovered] = useState(false);
|
|
176
176
|
const colors = themeStyles[theme];
|
|
177
177
|
const { title, description, href, icon, external } = item.config;
|
|
@@ -179,6 +179,7 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
|
|
|
179
179
|
...baseStyles.item,
|
|
180
180
|
...colors.item,
|
|
181
181
|
...(isExpanded ? colors.itemExpanded : {}),
|
|
182
|
+
...(!isLast ? { borderBottom: 'var(--sc-content-item-divider, none)' } : {}),
|
|
182
183
|
};
|
|
183
184
|
const headerStyle = {
|
|
184
185
|
...baseStyles.header,
|
|
@@ -187,7 +188,7 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
|
|
|
187
188
|
};
|
|
188
189
|
const chevronStyle = {
|
|
189
190
|
...baseStyles.chevron,
|
|
190
|
-
transform: isExpanded ? 'rotate(
|
|
191
|
+
transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
|
|
191
192
|
};
|
|
192
193
|
const bodyStyle = {
|
|
193
194
|
...baseStyles.body,
|
|
@@ -202,7 +203,7 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
|
|
|
202
203
|
onNavigate(href, external ?? false);
|
|
203
204
|
}
|
|
204
205
|
};
|
|
205
|
-
return (_jsxs("div", { style: itemStyle, "data-nav-tip-id": item.config.id, children: [_jsxs("button", { type: "button", style: headerStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [icon && _jsx("span", { style: baseStyles.icon, children: icon }), _jsx("span", { children: title }), _jsx("span", { style: chevronStyle, children: '\
|
|
206
|
+
return (_jsxs("div", { style: itemStyle, "data-nav-tip-id": item.config.id, children: [_jsxs("button", { type: "button", style: headerStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [icon && _jsx("span", { style: baseStyles.icon, children: icon }), _jsx("span", { children: title }), _jsx("span", { style: chevronStyle, children: '\u203A' })] }), _jsxs("div", { style: bodyStyle, "aria-hidden": !isExpanded, children: [_jsx("p", { style: baseStyles.description, children: description }), href && (_jsxs("a", { href: href, onClick: handleLinkClick, style: { ...baseStyles.linkButton, ...colors.linkButton }, target: external ? '_blank' : undefined, rel: external ? 'noopener noreferrer' : undefined, children: ["Go ", external ? '\u2197' : '\u2192'] }))] })] }));
|
|
206
207
|
}
|
|
207
208
|
// ============================================================================
|
|
208
209
|
// NavWidget Component
|
|
@@ -212,8 +213,8 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
|
|
|
212
213
|
*
|
|
213
214
|
* This component demonstrates the compositional action pattern:
|
|
214
215
|
* - Parent (NavWidget) receives `config.actions` array
|
|
215
|
-
* - Each action has optional `
|
|
216
|
-
* - Parent evaluates
|
|
216
|
+
* - Each action has optional `triggerWhen` for per-item visibility
|
|
217
|
+
* - Parent evaluates triggerWhen and filters visible tips
|
|
217
218
|
* - Parent manages expand state and re-rendering on context changes
|
|
218
219
|
*/
|
|
219
220
|
export function NavWidget({ config, runtime, instanceId }) {
|
|
@@ -228,7 +229,7 @@ export function NavWidget({ config, runtime, instanceId }) {
|
|
|
228
229
|
});
|
|
229
230
|
return unsubscribe;
|
|
230
231
|
}, [runtime.context]);
|
|
231
|
-
// Subscribe to accumulator changes for event_count-based
|
|
232
|
+
// Subscribe to accumulator changes for event_count-based triggerWhen
|
|
232
233
|
useEffect(() => {
|
|
233
234
|
if (!runtime.accumulator?.subscribe)
|
|
234
235
|
return;
|
|
@@ -236,50 +237,13 @@ export function NavWidget({ config, runtime, instanceId }) {
|
|
|
236
237
|
forceUpdate();
|
|
237
238
|
});
|
|
238
239
|
}, [runtime.accumulator]);
|
|
239
|
-
//
|
|
240
|
-
useEffect(() => {
|
|
241
|
-
if (!config.scope || !runtime.accumulator?.register)
|
|
242
|
-
return;
|
|
243
|
-
const { events: eventNames, urlContains, props: propFilters } = config.scope;
|
|
244
|
-
// Scan showWhen conditions for event_count keys
|
|
245
|
-
const keys = new Set();
|
|
246
|
-
for (const action of config.actions) {
|
|
247
|
-
if (action.showWhen?.type === 'rules') {
|
|
248
|
-
for (const rule of action.showWhen.rules) {
|
|
249
|
-
for (const cond of rule.conditions) {
|
|
250
|
-
if (cond.type === 'event_count' && cond.key) {
|
|
251
|
-
keys.add(cond.key);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
for (const key of keys) {
|
|
258
|
-
runtime.accumulator.register(key, (event) => {
|
|
259
|
-
if (!eventNames.includes(event.name))
|
|
260
|
-
return false;
|
|
261
|
-
if (urlContains) {
|
|
262
|
-
const pathname = String(event.props?.pathname ?? '');
|
|
263
|
-
if (!pathname.includes(urlContains))
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
if (propFilters) {
|
|
267
|
-
for (const [k, v] of Object.entries(propFilters)) {
|
|
268
|
-
if (event.props?.[k] !== v)
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return true;
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}, [config.scope, config.actions, runtime.accumulator]);
|
|
276
|
-
// Filter visible tips based on per-item showWhen
|
|
240
|
+
// Filter visible tips based on per-item triggerWhen
|
|
277
241
|
// biome-ignore lint/correctness/useExhaustiveDependencies: renderTick is intentionally included to force re-evaluation when the runtime's mutable context changes (subscribed above via forceUpdate)
|
|
278
242
|
const visibleTips = useMemo(() => config.actions.filter((tip) => {
|
|
279
|
-
if (!tip.
|
|
243
|
+
if (!tip.triggerWhen)
|
|
280
244
|
return true;
|
|
281
245
|
try {
|
|
282
|
-
const result = runtime.evaluateSync(tip.
|
|
246
|
+
const result = runtime.evaluateSync(tip.triggerWhen);
|
|
283
247
|
return result.value;
|
|
284
248
|
}
|
|
285
249
|
catch {
|
|
@@ -365,7 +329,9 @@ export function NavWidget({ config, runtime, instanceId }) {
|
|
|
365
329
|
window.open(href, '_blank', 'noopener,noreferrer');
|
|
366
330
|
}
|
|
367
331
|
else {
|
|
368
|
-
window.location.
|
|
332
|
+
const url = new URL(href, window.location.origin);
|
|
333
|
+
url.search = window.location.search;
|
|
334
|
+
window.location.href = url.toString();
|
|
369
335
|
}
|
|
370
336
|
}, [runtime.events, instanceId]);
|
|
371
337
|
// Compute container styles
|
|
@@ -382,10 +348,10 @@ export function NavWidget({ config, runtime, instanceId }) {
|
|
|
382
348
|
...themeStyles[resolvedTheme].emptyState,
|
|
383
349
|
};
|
|
384
350
|
// Render a list of nav tip items
|
|
385
|
-
const renderItems = (items) => items.map((tip) => (_jsx(NavTipItem, { item: tip, isExpanded: expandedIds.has(tip.config.id), onToggle: () => handleToggle(tip.config.id), onNavigate: handleNavigate, theme: resolvedTheme }, tip.config.id)));
|
|
351
|
+
const renderItems = (items) => items.map((tip, index) => (_jsx(NavTipItem, { item: tip, isExpanded: expandedIds.has(tip.config.id), isLast: index === items.length - 1, onToggle: () => handleToggle(tip.config.id), onNavigate: handleNavigate, theme: resolvedTheme }, tip.config.id)));
|
|
386
352
|
// Empty state
|
|
387
353
|
if (visibleTips.length === 0) {
|
|
388
|
-
return (_jsx("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx("div", { style: emptyStateStyle, children: "
|
|
354
|
+
return (_jsx("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx("div", { style: emptyStateStyle, children: "You're all set for now! We'll share helpful tips here when they're relevant to what you're doing." }) }));
|
|
389
355
|
}
|
|
390
356
|
return (_jsx("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx("div", { style: baseStyles.accordion, children: hasCategories
|
|
391
357
|
? Array.from(categoryGroups.entries()).map(([category, items]) => (_jsxs(React.Fragment, { children: [category && (_jsx("div", { style: categoryHeaderStyle, "data-category-header": category, children: category })), renderItems(items)] }, category ?? '__ungrouped')))
|
package/dist/cdn.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAA0B,MAAM,UAAU,CAAC;AAGlD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAA0B,MAAM,UAAU,CAAC;AAGlD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;2BAsCgpZ,CAAC;8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAtB1qZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;CAQjD,CAAC;AAaF,eAAe,QAAQ,CAAC"}
|
package/dist/editor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoBH,OAAO,EACL,KAAK,gBAAgB,EAKtB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoBH,OAAO,EACL,KAAK,gBAAgB,EAKtB,MAAM,SAAS,CAAC;AAmLjB,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAoZvE;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;;;;CAIvB,CAAC;AAEF,eAAO,MAAM,MAAM;;;;;;;CAGlB,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
package/dist/editor.js
CHANGED
|
@@ -6,7 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
6
6
|
* Displays a scannable list of tip cards with trigger, rationale,
|
|
7
7
|
* and inline editing. Includes detection badges and hover-to-highlight.
|
|
8
8
|
*/
|
|
9
|
-
import { DetectionBadge, DismissedSection, EditorBody, EditorCard, EditorFooter, EditorHeader, EditorInput, EditorLayout, EmptyState, GroupHeader, TriggerJourney,
|
|
9
|
+
import { DetectionBadge, DismissedSection, EditorBody, EditorCard, EditorFooter, EditorHeader, EditorInput, EditorLayout, EmptyState, GroupHeader, TriggerJourney, useTriggerWhenStatus, } from '@syntrologie/shared-editor-ui';
|
|
10
10
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
11
11
|
import { describeTrigger, summarizeNavItem } from './summarize';
|
|
12
12
|
import { isOwnAction, } from './types';
|
|
@@ -16,13 +16,13 @@ function isRuleStrategy(s) {
|
|
|
16
16
|
s.type === 'rules' &&
|
|
17
17
|
Array.isArray(s.rules));
|
|
18
18
|
}
|
|
19
|
-
function extractTargetingInfo(
|
|
20
|
-
if (!
|
|
19
|
+
function extractTargetingInfo(triggerWhen) {
|
|
20
|
+
if (!triggerWhen || !isRuleStrategy(triggerWhen)) {
|
|
21
21
|
return { pagePatterns: [], anchorSelectors: [], hasTargeting: false };
|
|
22
22
|
}
|
|
23
23
|
const pagePatterns = new Set();
|
|
24
24
|
const anchorSelectors = new Set();
|
|
25
|
-
for (const rule of
|
|
25
|
+
for (const rule of triggerWhen.rules) {
|
|
26
26
|
for (const cond of rule.conditions) {
|
|
27
27
|
const c = cond;
|
|
28
28
|
if (c.type === 'page_url' && typeof c.url === 'string') {
|
|
@@ -40,23 +40,32 @@ function extractTargetingInfo(showWhen) {
|
|
|
40
40
|
hasTargeting,
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
function extractFirstPage(
|
|
44
|
-
const info = extractTargetingInfo(
|
|
43
|
+
function extractFirstPage(triggerWhen) {
|
|
44
|
+
const info = extractTargetingInfo(triggerWhen);
|
|
45
45
|
return info.pagePatterns[0] || null;
|
|
46
46
|
}
|
|
47
|
-
function extractFirstAnchor(
|
|
48
|
-
const info = extractTargetingInfo(
|
|
47
|
+
function extractFirstAnchor(triggerWhen) {
|
|
48
|
+
const info = extractTargetingInfo(triggerWhen);
|
|
49
49
|
return info.anchorSelectors[0] || null;
|
|
50
50
|
}
|
|
51
|
+
/** Save a pending highlight selector to sessionStorage (inlined to avoid cross-package import). */
|
|
52
|
+
function savePendingHighlight(selector) {
|
|
53
|
+
try {
|
|
54
|
+
sessionStorage.setItem('syntro:editor:pending-highlight', selector);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Silently ignore
|
|
58
|
+
}
|
|
59
|
+
}
|
|
51
60
|
function flattenItems(config) {
|
|
52
61
|
const actions = (config.actions || []).filter(isOwnAction);
|
|
53
62
|
return actions.map((tip, i) => ({
|
|
54
63
|
key: String(i),
|
|
55
64
|
index: i,
|
|
56
65
|
summary: summarizeNavItem(tip),
|
|
57
|
-
trigger: describeTrigger(tip.
|
|
66
|
+
trigger: describeTrigger(tip.triggerWhen),
|
|
58
67
|
rationale: tip.rationale,
|
|
59
|
-
firstAnchor: extractFirstAnchor(tip.
|
|
68
|
+
firstAnchor: extractFirstAnchor(tip.triggerWhen),
|
|
60
69
|
tip,
|
|
61
70
|
}));
|
|
62
71
|
}
|
|
@@ -76,7 +85,7 @@ function useDetection(items, getCurrentRoute) {
|
|
|
76
85
|
const map = new Map();
|
|
77
86
|
const currentPath = getCurrentRoute();
|
|
78
87
|
for (const item of itemsRef.current) {
|
|
79
|
-
const targeting = extractTargetingInfo(item.tip.
|
|
88
|
+
const targeting = extractTargetingInfo(item.tip.triggerWhen);
|
|
80
89
|
// Check page match
|
|
81
90
|
let pageMatch = true;
|
|
82
91
|
if (targeting.pagePatterns.length > 0) {
|
|
@@ -162,12 +171,12 @@ export function NavEditor({ config, onChange, editor }) {
|
|
|
162
171
|
const activeItems = allItems.filter((item) => !dismissedKeys.has(item.key));
|
|
163
172
|
const dismissedItems = allItems.filter((item) => dismissedKeys.has(item.key));
|
|
164
173
|
const totalLinks = activeItems.length;
|
|
165
|
-
// Live
|
|
166
|
-
const
|
|
174
|
+
// Live triggerWhen status for condition diagnostics
|
|
175
|
+
const triggerWhenItems = useMemo(() => allItems.map((item) => ({
|
|
167
176
|
id: item.key,
|
|
168
|
-
|
|
177
|
+
triggerWhen: item.tip.triggerWhen,
|
|
169
178
|
})), [allItems]);
|
|
170
|
-
const
|
|
179
|
+
const triggerWhenStatuses = useTriggerWhenStatus(triggerWhenItems);
|
|
171
180
|
const detectionMap = useDetection(allItems, editor.getCurrentRoute);
|
|
172
181
|
const foundCount = activeItems.filter((item) => detectionMap.get(item.key)?.found).length;
|
|
173
182
|
const handleDismiss = useCallback((key) => {
|
|
@@ -189,10 +198,12 @@ export function NavEditor({ config, onChange, editor }) {
|
|
|
189
198
|
const handleCardBodyClick = useCallback((item) => {
|
|
190
199
|
setEditingKey(item.key);
|
|
191
200
|
}, []);
|
|
192
|
-
const handleTriggerClick = useCallback((item) => {
|
|
193
|
-
const pageUrl = extractFirstPage(item.tip.
|
|
201
|
+
const handleTriggerClick = useCallback(async (item) => {
|
|
202
|
+
const pageUrl = extractFirstPage(item.tip.triggerWhen);
|
|
194
203
|
if (pageUrl) {
|
|
195
|
-
|
|
204
|
+
if (item.firstAnchor)
|
|
205
|
+
savePendingHighlight(item.firstAnchor);
|
|
206
|
+
await editor.navigateTo(pageUrl);
|
|
196
207
|
}
|
|
197
208
|
if (item.firstAnchor) {
|
|
198
209
|
editor.highlightElement(item.firstAnchor);
|
|
@@ -236,6 +247,25 @@ export function NavEditor({ config, onChange, editor }) {
|
|
|
236
247
|
}
|
|
237
248
|
editor.publish();
|
|
238
249
|
}, [dismissedKeys, typedConfig, onChange, editor]);
|
|
250
|
+
const handleBadgeClick = useCallback(async (item) => {
|
|
251
|
+
const detection = detectionMap.get(item.key);
|
|
252
|
+
if (detection?.found && item.firstAnchor) {
|
|
253
|
+
editor.highlightElement(item.firstAnchor);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
const pageUrl = extractFirstPage(item.tip.triggerWhen);
|
|
257
|
+
if (pageUrl) {
|
|
258
|
+
if (item.firstAnchor)
|
|
259
|
+
savePendingHighlight(item.firstAnchor);
|
|
260
|
+
await editor.navigateTo(pageUrl);
|
|
261
|
+
if (item.firstAnchor)
|
|
262
|
+
editor.highlightElement(item.firstAnchor);
|
|
263
|
+
}
|
|
264
|
+
else if (item.firstAnchor) {
|
|
265
|
+
editor.highlightElement(item.firstAnchor);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}, [editor, detectionMap]);
|
|
239
269
|
const handleCardHover = useCallback((item) => {
|
|
240
270
|
setHoveredKey(item.key);
|
|
241
271
|
if (item.firstAnchor) {
|
|
@@ -272,10 +302,10 @@ export function NavEditor({ config, onChange, editor }) {
|
|
|
272
302
|
}, children: [_jsx("span", { children: '\u{1f4cd}' }), _jsx("span", { children: item.trigger })] })), _jsxs("div", { "data-card-body": true, role: "button", tabIndex: 0, className: "se-flex se-items-center se-gap-2 se-cursor-pointer", onClick: () => handleCardBodyClick(item), onKeyDown: (e) => {
|
|
273
303
|
if (e.key === 'Enter' || e.key === ' ')
|
|
274
304
|
handleCardBodyClick(item);
|
|
275
|
-
}, children: [_jsx(DetectionBadge, { found: detection?.found ?? false }), _jsx("span", { className: "se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap", children: item.summary }), _jsx("button", { type: "button", className: "se-py-0.5 se-px-1.5 se-rounded se-border-none se-bg-transparent se-text-slate-grey-7 se-text-sm se-cursor-pointer se-shrink-0 se-leading-none", onClick: (e) => {
|
|
305
|
+
}, children: [_jsx(DetectionBadge, { found: detection?.found ?? false, onClick: () => handleBadgeClick(item) }), _jsx("span", { className: "se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap", children: item.summary }), _jsx("button", { type: "button", className: "se-py-0.5 se-px-1.5 se-rounded se-border-none se-bg-transparent se-text-slate-grey-7 se-text-sm se-cursor-pointer se-shrink-0 se-leading-none", onClick: (e) => {
|
|
276
306
|
e.stopPropagation();
|
|
277
307
|
handleDismiss(item.key);
|
|
278
|
-
}, title: "Dismiss this tip", children: "\u00D7" })] }), _jsxs("div", { className: "se-text-[10px] se-text-slate-grey-7 se-mt-1", children: ["WHY: ", item.rationale ? item.rationale.why : 'N/A'] }), _jsx(TriggerJourney, { status:
|
|
308
|
+
}, title: "Dismiss this tip", children: "\u00D7" })] }), _jsxs("div", { className: "se-text-[10px] se-text-slate-grey-7 se-mt-1", children: ["WHY: ", item.rationale ? item.rationale.why : 'N/A'] }), _jsx(TriggerJourney, { status: triggerWhenStatuses.get(item.key) ?? null })] }, item.key));
|
|
279
309
|
})] })), dismissedItems.length > 0 && (_jsx(DismissedSection, { count: dismissedItems.length, children: dismissedItems.map((item) => (_jsxs("div", { className: "se-flex se-items-center se-gap-2 se-py-1.5 se-px-2.5 se-rounded-md se-border se-border-white/[0.03] se-bg-transparent se-mb-0.5 se-cursor-pointer se-text-xs se-text-slate-grey-6 se-opacity-60", children: [_jsx("span", { className: "se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap se-line-through", children: item.summary }), _jsx("button", { type: "button", className: "se-py-0.5 se-px-1.5 se-rounded se-border-none se-bg-transparent se-text-blue-5 se-text-[11px] se-cursor-pointer se-shrink-0 se-leading-none", onClick: (e) => {
|
|
280
310
|
e.stopPropagation();
|
|
281
311
|
handleRestore(item.key);
|
package/dist/runtime.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ export declare const runtime: {
|
|
|
67
67
|
/**
|
|
68
68
|
* Extract notify watcher entries from tile config props.
|
|
69
69
|
* The runtime evaluates these continuously (even with drawer closed)
|
|
70
|
-
* and publishes nav:tip_revealed when
|
|
70
|
+
* and publishes nav:tip_revealed when triggerWhen transitions false → true.
|
|
71
71
|
*/
|
|
72
72
|
notifyWatchers(props: Record<string, unknown>): {
|
|
73
73
|
id: string;
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,cAAc,EAEd,cAAc,EAEd,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,cAAc,EAEd,cAAc,EAEd,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CA+B1D,CAAC;AAgBF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CAoC1D,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;EAGZ,CAAC;AAMX;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;IAMlB;;OAEG;;;;;;;;IAGH;;OAEG;;;;;uBAqCy8R,CAAC;0BAA8B,CAAC;;;;;;;;;IAxB5+R;;;;OAIG;0BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;CAgB9C,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
package/dist/runtime.js
CHANGED
|
@@ -15,7 +15,8 @@ import { NavMountableWidget } from './NavWidget';
|
|
|
15
15
|
export const executeScrollTo = async (action, context) => {
|
|
16
16
|
const anchorEl = context.resolveAnchor(action.anchorId);
|
|
17
17
|
if (!anchorEl) {
|
|
18
|
-
|
|
18
|
+
console.error(`[adaptive-nav] Anchor not found for scrollTo, skipping: ${action.anchorId.selector}`);
|
|
19
|
+
return { cleanup: () => { } };
|
|
19
20
|
}
|
|
20
21
|
// Scroll to element
|
|
21
22
|
anchorEl.scrollIntoView({
|
|
@@ -131,15 +132,15 @@ export const runtime = {
|
|
|
131
132
|
/**
|
|
132
133
|
* Extract notify watcher entries from tile config props.
|
|
133
134
|
* The runtime evaluates these continuously (even with drawer closed)
|
|
134
|
-
* and publishes nav:tip_revealed when
|
|
135
|
+
* and publishes nav:tip_revealed when triggerWhen transitions false → true.
|
|
135
136
|
*/
|
|
136
137
|
notifyWatchers(props) {
|
|
137
138
|
const actions = (props.actions ?? []);
|
|
138
139
|
return actions
|
|
139
|
-
.filter((a) => a.notify && a.
|
|
140
|
+
.filter((a) => a.notify && a.triggerWhen)
|
|
140
141
|
.map((a) => ({
|
|
141
142
|
id: `nav:${a.config.id}`,
|
|
142
|
-
strategy: a.
|
|
143
|
+
strategy: a.triggerWhen,
|
|
143
144
|
eventName: 'nav:tip_revealed',
|
|
144
145
|
eventProps: {
|
|
145
146
|
tipId: a.config.id,
|