@skhema/web-component 0.0.9
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/README.md +57 -0
- package/dist/cdn.d.ts +4 -0
- package/dist/cdn.d.ts.map +1 -0
- package/dist/components/SkhemaElement.d.ts +34 -0
- package/dist/components/SkhemaElement.d.ts.map +1 -0
- package/dist/components/types.d.ts +43 -0
- package/dist/components/types.d.ts.map +1 -0
- package/dist/index.cjs +675 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +675 -0
- package/dist/index.es.js.map +1 -0
- package/dist/utils/analytics.d.ts +7 -0
- package/dist/utils/analytics.d.ts.map +1 -0
- package/dist/utils/seo.d.ts +12 -0
- package/dist/utils/seo.d.ts.map +1 -0
- package/dist/utils/validation.d.ts +12 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/web-component.min.js +2 -0
- package/dist/web-component.min.js.map +1 -0
- package/package.json +66 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
+
const types = require("@skhema/types");
|
|
4
|
+
function isValidElementType(elementType) {
|
|
5
|
+
const validTypes = Object.values(types.ELEMENT_TYPES).map((type) => type.value);
|
|
6
|
+
return validTypes.includes(elementType);
|
|
7
|
+
}
|
|
8
|
+
function validateAttributes(element) {
|
|
9
|
+
const errors = [];
|
|
10
|
+
const elementType = element.getAttribute("element-type");
|
|
11
|
+
const contributorId = element.getAttribute("contributor-id");
|
|
12
|
+
if (!elementType) {
|
|
13
|
+
errors.push("Missing required attribute: element-type");
|
|
14
|
+
} else if (!isValidElementType(elementType)) {
|
|
15
|
+
const validTypes = Object.values(types.ELEMENT_TYPES).map((t) => t.value).join(", ");
|
|
16
|
+
errors.push(`Invalid element-type "${elementType}". Valid types: ${validTypes}`);
|
|
17
|
+
}
|
|
18
|
+
if (!contributorId) {
|
|
19
|
+
errors.push("Missing required attribute: contributor-id");
|
|
20
|
+
} else if (contributorId.trim().length === 0) {
|
|
21
|
+
errors.push("contributor-id cannot be empty");
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
isValid: errors.length === 0,
|
|
25
|
+
errors,
|
|
26
|
+
elementType: isValidElementType(elementType || "") ? elementType : void 0,
|
|
27
|
+
contributorId: contributorId || void 0
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function getElementTypeLabel(elementType) {
|
|
31
|
+
const type = Object.values(types.ELEMENT_TYPES).find((t) => t.value === elementType);
|
|
32
|
+
return (type == null ? void 0 : type.label) || elementType;
|
|
33
|
+
}
|
|
34
|
+
function getElementTypeAcronym(elementType) {
|
|
35
|
+
const type = Object.values(types.ELEMENT_TYPES).find((t) => t.value === elementType);
|
|
36
|
+
return (type == null ? void 0 : type.acronym) || elementType.substring(0, 2).toUpperCase();
|
|
37
|
+
}
|
|
38
|
+
function generateContentHash$1(content) {
|
|
39
|
+
let hash = 0;
|
|
40
|
+
const cleanContent = content.trim().substring(0, 200);
|
|
41
|
+
for (let i = 0; i < cleanContent.length; i++) {
|
|
42
|
+
const char = cleanContent.charCodeAt(i);
|
|
43
|
+
hash = (hash << 5) - hash + char;
|
|
44
|
+
hash = hash & hash;
|
|
45
|
+
}
|
|
46
|
+
return Math.abs(hash).toString(36).substring(0, 12);
|
|
47
|
+
}
|
|
48
|
+
async function trackEmbedLoad(analytics) {
|
|
49
|
+
try {
|
|
50
|
+
const data = new URLSearchParams({
|
|
51
|
+
contributor_id: analytics.contributorId,
|
|
52
|
+
element_type: analytics.elementType,
|
|
53
|
+
content_hash: analytics.contentHash,
|
|
54
|
+
page_url: analytics.pageUrl,
|
|
55
|
+
page_title: analytics.pageTitle || "",
|
|
56
|
+
timestamp: analytics.timestamp.toString(),
|
|
57
|
+
user_agent: analytics.userAgent || ""
|
|
58
|
+
});
|
|
59
|
+
if (navigator.sendBeacon) {
|
|
60
|
+
navigator.sendBeacon("https://api.skhema.com/api:XGdoUqHx/component/embed", data);
|
|
61
|
+
} else {
|
|
62
|
+
fetch("https://api.skhema.com/api:XGdoUqHx/component/embed", {
|
|
63
|
+
method: "POST",
|
|
64
|
+
body: data,
|
|
65
|
+
keepalive: true
|
|
66
|
+
}).catch(() => {
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.debug("Analytics tracking failed:", error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function trackClick(contentData) {
|
|
74
|
+
try {
|
|
75
|
+
const data = {
|
|
76
|
+
contributor_id: contentData.contributor_id,
|
|
77
|
+
element_type: contentData.element_type,
|
|
78
|
+
content_hash: contentData.content_hash,
|
|
79
|
+
source_url: contentData.source_url,
|
|
80
|
+
timestamp: contentData.timestamp
|
|
81
|
+
};
|
|
82
|
+
if (navigator.sendBeacon) {
|
|
83
|
+
navigator.sendBeacon(
|
|
84
|
+
"https://api.skhema.com/api:XGdoUqHx/component/click",
|
|
85
|
+
JSON.stringify(data)
|
|
86
|
+
);
|
|
87
|
+
} else {
|
|
88
|
+
fetch("https://api.skhema.com/api:XGdoUqHx/component/click", {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: { "Content-Type": "application/json" },
|
|
91
|
+
body: JSON.stringify(data),
|
|
92
|
+
keepalive: true
|
|
93
|
+
}).catch(() => {
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.debug("Click tracking failed:", error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function shouldTrackAnalytics(element) {
|
|
101
|
+
const trackAnalytics = element.getAttribute("track-analytics");
|
|
102
|
+
return trackAnalytics !== "false";
|
|
103
|
+
}
|
|
104
|
+
function generateStructuredData(content, elementType, contributorId, sourceUrl) {
|
|
105
|
+
return {
|
|
106
|
+
"@context": "https://schema.org",
|
|
107
|
+
"@type": "AnalysisContent",
|
|
108
|
+
"text": content,
|
|
109
|
+
"analysisType": elementType,
|
|
110
|
+
"category": getElementTypeLabel(elementType),
|
|
111
|
+
"contributor": contributorId,
|
|
112
|
+
"url": generateRedirectUrl(content, elementType, contributorId),
|
|
113
|
+
"provider": {
|
|
114
|
+
"@type": "Organization",
|
|
115
|
+
"name": "Skhema",
|
|
116
|
+
"url": "https://skhema.com"
|
|
117
|
+
},
|
|
118
|
+
"isPartOf": {
|
|
119
|
+
"@type": "WebPage",
|
|
120
|
+
"url": sourceUrl
|
|
121
|
+
},
|
|
122
|
+
"dateCreated": (/* @__PURE__ */ new Date()).toISOString(),
|
|
123
|
+
"platform": "Skhema"
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function generateRedirectUrl(content, elementType, contributorId, options = {}) {
|
|
127
|
+
const baseUrl = options.baseUrl || "https://api.skhema.com/api:XGdoUqHx/component/redirect";
|
|
128
|
+
const contentHash = generateContentHash(content);
|
|
129
|
+
const sourceUrl = encodeURIComponent(window.location.href);
|
|
130
|
+
const timestamp = Date.now();
|
|
131
|
+
const params = new URLSearchParams({
|
|
132
|
+
source: sourceUrl,
|
|
133
|
+
t: timestamp.toString(),
|
|
134
|
+
utm_source: options.utmSource || "web_component",
|
|
135
|
+
utm_medium: options.utmMedium || "embedded",
|
|
136
|
+
utm_campaign: options.utmCampaign || elementType,
|
|
137
|
+
utm_content: contributorId
|
|
138
|
+
});
|
|
139
|
+
return `${baseUrl}?contributor_id=${contributorId}&element_type=${elementType}&content_hash=${contentHash}&${params.toString()}`;
|
|
140
|
+
}
|
|
141
|
+
function generateContentHash(content) {
|
|
142
|
+
return btoa(content.substring(0, 50)).replace(/[^a-zA-Z0-9]/g, "").substring(0, 12);
|
|
143
|
+
}
|
|
144
|
+
function createMetaTags(content, elementType, contributorId) {
|
|
145
|
+
const label = getElementTypeLabel(elementType);
|
|
146
|
+
return `
|
|
147
|
+
<div itemscope itemtype="https://schema.org/AnalysisContent" style="display:none;">
|
|
148
|
+
<meta itemprop="analysisType" content="${elementType}">
|
|
149
|
+
<meta itemprop="text" content="${content}">
|
|
150
|
+
<meta itemprop="contributor" content="${contributorId}">
|
|
151
|
+
<meta itemprop="category" content="${label}">
|
|
152
|
+
<meta itemprop="platform" content="Skhema">
|
|
153
|
+
</div>
|
|
154
|
+
`;
|
|
155
|
+
}
|
|
156
|
+
function createAriaAttributes(elementType) {
|
|
157
|
+
const label = getElementTypeLabel(elementType);
|
|
158
|
+
return {
|
|
159
|
+
"role": "article",
|
|
160
|
+
"aria-label": `${label} - Strategic insight`,
|
|
161
|
+
"aria-describedby": "skhema-description"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const styles = `
|
|
165
|
+
:host {
|
|
166
|
+
/* Skhema Brand Colors - matching UI library */
|
|
167
|
+
--skhema-primary: hsl(344 57% 54%); /* #cd476a */
|
|
168
|
+
--skhema-primary-hover: hsl(344 50% 47%); /* #b53d5e */
|
|
169
|
+
--skhema-primary-pressed: hsl(343 50% 41%); /* #9d3552 */
|
|
170
|
+
--skhema-secondary: hsl(345 100% 75%); /* #ff82a2 */
|
|
171
|
+
--skhema-gradient: linear-gradient(135deg, hsl(344 57% 54%) 0%, hsl(345 100% 75%) 100%);
|
|
172
|
+
|
|
173
|
+
/* Light mode colors */
|
|
174
|
+
--skhema-bg: hsl(0 0% 100%);
|
|
175
|
+
--skhema-card: hsl(0 0% 100%);
|
|
176
|
+
--skhema-border: hsl(214.3 31.8% 91.4%);
|
|
177
|
+
--skhema-text: hsl(222.2 84% 4.9%);
|
|
178
|
+
--skhema-text-muted: hsl(215.4 16.3% 46.9%);
|
|
179
|
+
--skhema-accent: hsl(210 40% 96%);
|
|
180
|
+
|
|
181
|
+
/* Shadows matching UI library */
|
|
182
|
+
--skhema-shadow: 0 1px 3px 0 hsl(0 0 0 / 0.1), 0 1px 2px -1px hsl(0 0 0 / 0.1);
|
|
183
|
+
--skhema-shadow-md: 0 4px 6px -1px hsl(0 0 0 / 0.1), 0 2px 4px -2px hsl(0 0 0 / 0.1);
|
|
184
|
+
--skhema-shadow-lg: 0 10px 15px -3px hsl(0 0 0 / 0.1), 0 4px 6px -4px hsl(0 0 0 / 0.1);
|
|
185
|
+
--skhema-radius: 0.375rem;
|
|
186
|
+
|
|
187
|
+
display: block;
|
|
188
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif;
|
|
189
|
+
line-height: 1.5;
|
|
190
|
+
color: var(--skhema-text);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
:host([theme="dark"]) {
|
|
194
|
+
/* Dark mode colors */
|
|
195
|
+
--skhema-bg: hsl(222.2 84% 4.9%);
|
|
196
|
+
--skhema-card: hsl(222.2 84% 4.9%);
|
|
197
|
+
--skhema-border: hsl(217.2 32.6% 17.5%);
|
|
198
|
+
--skhema-text: hsl(210 40% 98%);
|
|
199
|
+
--skhema-text-muted: hsl(215 20.2% 65.1%);
|
|
200
|
+
--skhema-accent: hsl(217.2 32.6% 17.5%);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Main component card - inspired by your design */
|
|
204
|
+
.skhema-insight-card {
|
|
205
|
+
position: relative;
|
|
206
|
+
background: var(--skhema-card);
|
|
207
|
+
border: 1px solid var(--skhema-border);
|
|
208
|
+
border-radius: calc(var(--skhema-radius) * 2);
|
|
209
|
+
padding: 16px;
|
|
210
|
+
box-shadow: var(--skhema-shadow);
|
|
211
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
212
|
+
max-width: 600px;
|
|
213
|
+
margin: 8px 0;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.skhema-insight-card:hover {
|
|
217
|
+
box-shadow: var(--skhema-shadow-lg);
|
|
218
|
+
border-color: var(--skhema-primary);
|
|
219
|
+
transform: translateY(-1px);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Header section with contributor info */
|
|
223
|
+
.skhema-header {
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
justify-content: space-between;
|
|
227
|
+
margin-bottom: 12px;
|
|
228
|
+
gap: 12px;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.skhema-contributor {
|
|
232
|
+
display: flex;
|
|
233
|
+
align-items: center;
|
|
234
|
+
gap: 8px;
|
|
235
|
+
flex: 1;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.skhema-avatar {
|
|
239
|
+
width: 32px;
|
|
240
|
+
height: 32px;
|
|
241
|
+
border-radius: 50%;
|
|
242
|
+
background: var(--skhema-gradient);
|
|
243
|
+
display: flex;
|
|
244
|
+
align-items: center;
|
|
245
|
+
justify-content: center;
|
|
246
|
+
font-weight: 600;
|
|
247
|
+
font-size: 14px;
|
|
248
|
+
color: white;
|
|
249
|
+
flex-shrink: 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.skhema-contributor-info {
|
|
253
|
+
min-width: 0;
|
|
254
|
+
flex: 1;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.skhema-contributor-name {
|
|
258
|
+
font-weight: 500;
|
|
259
|
+
font-size: 14px;
|
|
260
|
+
color: var(--skhema-text);
|
|
261
|
+
margin: 0;
|
|
262
|
+
line-height: 1.2;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.skhema-contributor-role {
|
|
266
|
+
font-size: 12px;
|
|
267
|
+
color: var(--skhema-text-muted);
|
|
268
|
+
margin: 0;
|
|
269
|
+
line-height: 1.2;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* Element type badge */
|
|
273
|
+
.skhema-element-badge {
|
|
274
|
+
display: inline-flex;
|
|
275
|
+
align-items: center;
|
|
276
|
+
padding: 4px 8px;
|
|
277
|
+
background: var(--skhema-accent);
|
|
278
|
+
border: 1px solid var(--skhema-border);
|
|
279
|
+
border-radius: var(--skhema-radius);
|
|
280
|
+
font-size: 11px;
|
|
281
|
+
font-weight: 500;
|
|
282
|
+
color: var(--skhema-text-muted);
|
|
283
|
+
text-transform: capitalize;
|
|
284
|
+
white-space: nowrap;
|
|
285
|
+
flex-shrink: 0;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Content section */
|
|
289
|
+
.skhema-content {
|
|
290
|
+
margin: 12px 0 16px 0;
|
|
291
|
+
padding: 0;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.skhema-content-text {
|
|
295
|
+
font-size: 15px;
|
|
296
|
+
line-height: 1.6;
|
|
297
|
+
color: var(--skhema-text);
|
|
298
|
+
margin: 0;
|
|
299
|
+
font-style: italic;
|
|
300
|
+
position: relative;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.skhema-content-text::before {
|
|
304
|
+
content: '"';
|
|
305
|
+
color: var(--skhema-primary);
|
|
306
|
+
font-size: 20px;
|
|
307
|
+
font-weight: 600;
|
|
308
|
+
margin-right: 4px;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.skhema-content-text::after {
|
|
312
|
+
content: '"';
|
|
313
|
+
color: var(--skhema-primary);
|
|
314
|
+
font-size: 20px;
|
|
315
|
+
font-weight: 600;
|
|
316
|
+
margin-left: 4px;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* Footer section */
|
|
320
|
+
.skhema-footer {
|
|
321
|
+
display: flex;
|
|
322
|
+
align-items: center;
|
|
323
|
+
justify-content: space-between;
|
|
324
|
+
gap: 12px;
|
|
325
|
+
padding-top: 12px;
|
|
326
|
+
border-top: 1px solid var(--skhema-border);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.skhema-attribution {
|
|
330
|
+
font-size: 11px;
|
|
331
|
+
color: var(--skhema-text-muted);
|
|
332
|
+
display: flex;
|
|
333
|
+
align-items: center;
|
|
334
|
+
gap: 4px;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.skhema-attribution a {
|
|
338
|
+
color: var(--skhema-primary);
|
|
339
|
+
text-decoration: none;
|
|
340
|
+
font-weight: 500;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.skhema-attribution a:hover {
|
|
344
|
+
text-decoration: underline;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* Save button with gradient and arrow */
|
|
348
|
+
.skhema-save-btn {
|
|
349
|
+
display: inline-flex;
|
|
350
|
+
align-items: center;
|
|
351
|
+
gap: 6px;
|
|
352
|
+
background: var(--skhema-gradient);
|
|
353
|
+
color: white;
|
|
354
|
+
border: none;
|
|
355
|
+
padding: 8px 16px;
|
|
356
|
+
border-radius: var(--skhema-radius);
|
|
357
|
+
font-size: 13px;
|
|
358
|
+
font-weight: 500;
|
|
359
|
+
text-decoration: none;
|
|
360
|
+
cursor: pointer;
|
|
361
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
362
|
+
box-shadow: var(--skhema-shadow);
|
|
363
|
+
white-space: nowrap;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.skhema-save-btn:hover {
|
|
367
|
+
transform: translateY(-1px);
|
|
368
|
+
box-shadow: var(--skhema-shadow-md);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.skhema-save-btn:active {
|
|
372
|
+
transform: translateY(0);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.skhema-save-btn:focus {
|
|
376
|
+
outline: 2px solid var(--skhema-primary);
|
|
377
|
+
outline-offset: 2px;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.skhema-save-btn::after {
|
|
381
|
+
content: '→';
|
|
382
|
+
transition: transform 0.2s ease;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.skhema-save-btn:hover::after {
|
|
386
|
+
transform: translateX(2px);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* Error state */
|
|
390
|
+
.skhema-error {
|
|
391
|
+
background: hsl(0 93% 94%);
|
|
392
|
+
border: 1px solid hsl(0 84% 60%);
|
|
393
|
+
color: hsl(0 74% 42%);
|
|
394
|
+
padding: 12px;
|
|
395
|
+
border-radius: var(--skhema-radius);
|
|
396
|
+
font-size: 13px;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.skhema-error-title {
|
|
400
|
+
font-weight: 600;
|
|
401
|
+
margin-bottom: 8px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.skhema-error-list {
|
|
405
|
+
margin: 0;
|
|
406
|
+
padding-left: 16px;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* Loading state */
|
|
410
|
+
.skhema-loading {
|
|
411
|
+
background: var(--skhema-accent);
|
|
412
|
+
border: 1px solid var(--skhema-border);
|
|
413
|
+
padding: 12px;
|
|
414
|
+
border-radius: var(--skhema-radius);
|
|
415
|
+
color: var(--skhema-text-muted);
|
|
416
|
+
font-size: 13px;
|
|
417
|
+
text-align: center;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.skhema-loading::after {
|
|
421
|
+
content: '...';
|
|
422
|
+
animation: loading 1.5s infinite;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
@keyframes loading {
|
|
426
|
+
0%, 33% { content: '...'; }
|
|
427
|
+
66% { content: '..'; }
|
|
428
|
+
100% { content: '.'; }
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/* Responsive design */
|
|
432
|
+
@media (max-width: 640px) {
|
|
433
|
+
.skhema-insight-card {
|
|
434
|
+
margin: 4px 0;
|
|
435
|
+
padding: 12px;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.skhema-header {
|
|
439
|
+
flex-direction: column;
|
|
440
|
+
align-items: flex-start;
|
|
441
|
+
gap: 8px;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.skhema-footer {
|
|
445
|
+
flex-direction: column;
|
|
446
|
+
align-items: stretch;
|
|
447
|
+
gap: 8px;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.skhema-save-btn {
|
|
451
|
+
justify-content: center;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* Accessibility */
|
|
456
|
+
@media (prefers-reduced-motion: reduce) {
|
|
457
|
+
.skhema-insight-card,
|
|
458
|
+
.skhema-save-btn {
|
|
459
|
+
transition: none;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.skhema-save-btn::after {
|
|
463
|
+
transition: none;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.skhema-save-btn:hover::after {
|
|
467
|
+
transform: none;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.skhema-structured-data {
|
|
472
|
+
display: none !important;
|
|
473
|
+
}
|
|
474
|
+
`;
|
|
475
|
+
class SkhemaElement extends HTMLElement {
|
|
476
|
+
constructor() {
|
|
477
|
+
super();
|
|
478
|
+
this.contentData = null;
|
|
479
|
+
this.componentConnected = false;
|
|
480
|
+
this.shadow = this.attachShadow({ mode: "closed" });
|
|
481
|
+
}
|
|
482
|
+
static get observedAttributes() {
|
|
483
|
+
return ["element-type", "contributor-id", "content", "source-url", "theme", "track-analytics"];
|
|
484
|
+
}
|
|
485
|
+
connectedCallback() {
|
|
486
|
+
if (this.componentConnected) return;
|
|
487
|
+
this.componentConnected = true;
|
|
488
|
+
try {
|
|
489
|
+
this.render();
|
|
490
|
+
this.trackLoad();
|
|
491
|
+
} catch (error) {
|
|
492
|
+
this.renderError("Failed to initialize component", error);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
attributeChangedCallback(_name, oldValue, newValue) {
|
|
496
|
+
if (oldValue !== newValue && this.componentConnected) {
|
|
497
|
+
this.render();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
render() {
|
|
501
|
+
const validation = validateAttributes(this);
|
|
502
|
+
if (!validation.isValid) {
|
|
503
|
+
this.renderError("Invalid component attributes", validation.errors);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const content = this.getContent();
|
|
507
|
+
if (!content.trim()) {
|
|
508
|
+
this.renderError("Component requires content", ["Add content between the opening and closing tags, or use the content attribute"]);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
this.contentData = {
|
|
512
|
+
contributor_id: validation.contributorId,
|
|
513
|
+
element_type: validation.elementType,
|
|
514
|
+
content,
|
|
515
|
+
content_hash: generateContentHash$1(content),
|
|
516
|
+
source_url: this.getAttribute("source-url") || window.location.href,
|
|
517
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
518
|
+
page_title: document.title
|
|
519
|
+
};
|
|
520
|
+
this.renderContent();
|
|
521
|
+
this.addStructuredData();
|
|
522
|
+
}
|
|
523
|
+
getContent() {
|
|
524
|
+
return this.getAttribute("content") || this.textContent || "";
|
|
525
|
+
}
|
|
526
|
+
renderContent() {
|
|
527
|
+
if (!this.contentData) return;
|
|
528
|
+
const { element_type, contributor_id, content } = this.contentData;
|
|
529
|
+
const label = getElementTypeLabel(element_type);
|
|
530
|
+
const redirectUrl = generateRedirectUrl(content, element_type, contributor_id);
|
|
531
|
+
const theme = this.getAttribute("theme") || "auto";
|
|
532
|
+
const displayName = this.formatContributorName(contributor_id);
|
|
533
|
+
const initials = this.getInitials(displayName);
|
|
534
|
+
const ariaAttrs = createAriaAttributes(element_type);
|
|
535
|
+
Object.entries(ariaAttrs).forEach(([key, value]) => {
|
|
536
|
+
this.setAttribute(key, value);
|
|
537
|
+
});
|
|
538
|
+
this.shadow.innerHTML = `
|
|
539
|
+
<style>${styles}</style>
|
|
540
|
+
|
|
541
|
+
<div class="skhema-insight-card" data-theme="${theme}">
|
|
542
|
+
<div class="skhema-header">
|
|
543
|
+
<div class="skhema-contributor">
|
|
544
|
+
<div class="skhema-avatar" title="${displayName}">
|
|
545
|
+
${initials}
|
|
546
|
+
</div>
|
|
547
|
+
<div class="skhema-contributor-info">
|
|
548
|
+
<div class="skhema-contributor-name">${displayName}</div>
|
|
549
|
+
<div class="skhema-contributor-role">Strategy Insight</div>
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
<div class="skhema-element-badge" title="${label}">
|
|
553
|
+
${label}
|
|
554
|
+
</div>
|
|
555
|
+
</div>
|
|
556
|
+
|
|
557
|
+
<div class="skhema-content">
|
|
558
|
+
<div class="skhema-content-text">${content}</div>
|
|
559
|
+
</div>
|
|
560
|
+
|
|
561
|
+
<div class="skhema-footer">
|
|
562
|
+
<div class="skhema-attribution">
|
|
563
|
+
Powered by <a href="https://skhema.com" target="_blank" rel="noopener noreferrer">Skhema</a>
|
|
564
|
+
</div>
|
|
565
|
+
<a href="${redirectUrl}"
|
|
566
|
+
class="skhema-save-btn"
|
|
567
|
+
target="_blank"
|
|
568
|
+
rel="noopener noreferrer"
|
|
569
|
+
title="Save this insight to Skhema">
|
|
570
|
+
Save to Skhema
|
|
571
|
+
</a>
|
|
572
|
+
</div>
|
|
573
|
+
</div>
|
|
574
|
+
`;
|
|
575
|
+
const saveBtn = this.shadow.querySelector(".skhema-save-btn");
|
|
576
|
+
if (saveBtn) {
|
|
577
|
+
saveBtn.addEventListener("click", (event) => {
|
|
578
|
+
this.handleSaveClick(event);
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
formatContributorName(contributorId) {
|
|
583
|
+
return contributorId.split(/[_-]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
584
|
+
}
|
|
585
|
+
getInitials(name) {
|
|
586
|
+
return name.split(" ").map((word) => word.charAt(0)).join("").toUpperCase().substring(0, 2);
|
|
587
|
+
}
|
|
588
|
+
renderError(title, errors) {
|
|
589
|
+
const errorList = Array.isArray(errors) ? errors : [String(errors)];
|
|
590
|
+
this.shadow.innerHTML = `
|
|
591
|
+
<style>${styles}</style>
|
|
592
|
+
|
|
593
|
+
<div class="skhema-insight-card">
|
|
594
|
+
<div class="skhema-error">
|
|
595
|
+
<div class="skhema-error-title">Skhema Component Error: ${title}</div>
|
|
596
|
+
<ul class="skhema-error-list">
|
|
597
|
+
${errorList.map((error) => `<li>${error}</li>`).join("")}
|
|
598
|
+
</ul>
|
|
599
|
+
</div>
|
|
600
|
+
</div>
|
|
601
|
+
`;
|
|
602
|
+
this.dispatchEvent(new CustomEvent("skhema:error", {
|
|
603
|
+
detail: { error: title, details: errors },
|
|
604
|
+
bubbles: true
|
|
605
|
+
}));
|
|
606
|
+
}
|
|
607
|
+
addStructuredData() {
|
|
608
|
+
if (!this.contentData) return;
|
|
609
|
+
const { content, element_type, contributor_id, source_url } = this.contentData;
|
|
610
|
+
const structuredData = generateStructuredData(content, element_type, contributor_id, source_url);
|
|
611
|
+
const script = document.createElement("script");
|
|
612
|
+
script.type = "application/ld+json";
|
|
613
|
+
script.textContent = JSON.stringify(structuredData);
|
|
614
|
+
script.className = "skhema-structured-data";
|
|
615
|
+
document.head.appendChild(script);
|
|
616
|
+
const metaDiv = document.createElement("div");
|
|
617
|
+
metaDiv.innerHTML = createMetaTags(content, element_type, contributor_id);
|
|
618
|
+
metaDiv.className = "skhema-structured-data";
|
|
619
|
+
document.body.appendChild(metaDiv);
|
|
620
|
+
}
|
|
621
|
+
async trackLoad() {
|
|
622
|
+
if (!shouldTrackAnalytics(this) || !this.contentData) return;
|
|
623
|
+
const analytics = {
|
|
624
|
+
contributorId: this.contentData.contributor_id,
|
|
625
|
+
elementType: this.contentData.element_type,
|
|
626
|
+
contentHash: this.contentData.content_hash,
|
|
627
|
+
pageUrl: window.location.href,
|
|
628
|
+
pageTitle: document.title,
|
|
629
|
+
timestamp: Date.now(),
|
|
630
|
+
userAgent: navigator.userAgent
|
|
631
|
+
};
|
|
632
|
+
await trackEmbedLoad(analytics);
|
|
633
|
+
this.dispatchEvent(new CustomEvent("skhema:load", {
|
|
634
|
+
detail: analytics,
|
|
635
|
+
bubbles: true
|
|
636
|
+
}));
|
|
637
|
+
}
|
|
638
|
+
async handleSaveClick(_event) {
|
|
639
|
+
if (!this.contentData) return;
|
|
640
|
+
if (shouldTrackAnalytics(this)) {
|
|
641
|
+
await trackClick(this.contentData);
|
|
642
|
+
}
|
|
643
|
+
this.dispatchEvent(new CustomEvent("skhema:click", {
|
|
644
|
+
detail: this.contentData,
|
|
645
|
+
bubbles: true
|
|
646
|
+
}));
|
|
647
|
+
}
|
|
648
|
+
// Public API methods
|
|
649
|
+
getContentData() {
|
|
650
|
+
return this.contentData;
|
|
651
|
+
}
|
|
652
|
+
refresh() {
|
|
653
|
+
this.render();
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function registerSkhemaElement() {
|
|
657
|
+
if (typeof window !== "undefined" && !customElements.get("skhema-element")) {
|
|
658
|
+
customElements.define("skhema-element", SkhemaElement);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (typeof window !== "undefined" && !customElements.get("skhema-element")) {
|
|
662
|
+
customElements.define("skhema-element", SkhemaElement);
|
|
663
|
+
}
|
|
664
|
+
exports.SkhemaElement = SkhemaElement;
|
|
665
|
+
exports.default = SkhemaElement;
|
|
666
|
+
exports.generateContentHash = generateContentHash$1;
|
|
667
|
+
exports.generateRedirectUrl = generateRedirectUrl;
|
|
668
|
+
exports.generateStructuredData = generateStructuredData;
|
|
669
|
+
exports.getElementTypeAcronym = getElementTypeAcronym;
|
|
670
|
+
exports.getElementTypeLabel = getElementTypeLabel;
|
|
671
|
+
exports.isValidElementType = isValidElementType;
|
|
672
|
+
exports.registerSkhemaElement = registerSkhemaElement;
|
|
673
|
+
exports.shouldTrackAnalytics = shouldTrackAnalytics;
|
|
674
|
+
exports.validateAttributes = validateAttributes;
|
|
675
|
+
//# sourceMappingURL=index.cjs.map
|