@yoamigo.com/core 0.4.7 → 1.0.0
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/cart-storage-DFdGPcwm.d.ts +176 -0
- package/dist/index.d.ts +1198 -39
- package/dist/index.js +2410 -468
- package/dist/lib.d.ts +93 -2
- package/dist/lib.js +337 -4
- package/dist/prod.d.ts +2 -2
- package/dist/prod.js +74 -1
- package/dist/router.d.ts +80 -2
- package/dist/router.js +74 -1
- package/package.json +1 -1
- package/dist/builder-selection-CYP91nRu.d.ts +0 -6
package/dist/index.js
CHANGED
|
@@ -895,7 +895,7 @@ var BuilderSelectionManager = class {
|
|
|
895
895
|
const navLinks = document.querySelectorAll("nav a[href]");
|
|
896
896
|
navLinks.forEach((link) => {
|
|
897
897
|
const href = link.getAttribute("href");
|
|
898
|
-
if (href
|
|
898
|
+
if (href?.startsWith("/")) {
|
|
899
899
|
const path = href.split("?")[0].replace(/\/$/, "") || "/";
|
|
900
900
|
routes.add(path);
|
|
901
901
|
}
|
|
@@ -907,7 +907,7 @@ var BuilderSelectionManager = class {
|
|
|
907
907
|
* Non-blocking: uses requestIdleCallback between pages
|
|
908
908
|
* Transfers ArrayBuffer directly (zero-copy) for performance
|
|
909
909
|
*/
|
|
910
|
-
async captureAllPagesScreenshots(checkpointId,
|
|
910
|
+
async captureAllPagesScreenshots(checkpointId, _viewport) {
|
|
911
911
|
console.log("[BuilderSelection] Starting multi-page capture for checkpoint:", checkpointId);
|
|
912
912
|
const routes = this.getAllRoutes();
|
|
913
913
|
const originalPath = window.location.pathname;
|
|
@@ -923,7 +923,8 @@ var BuilderSelectionManager = class {
|
|
|
923
923
|
try {
|
|
924
924
|
await new Promise((resolve) => {
|
|
925
925
|
if ("requestIdleCallback" in window) {
|
|
926
|
-
|
|
926
|
+
;
|
|
927
|
+
window.requestIdleCallback(() => resolve(), { timeout: 1e3 });
|
|
927
928
|
} else {
|
|
928
929
|
setTimeout(resolve, 50);
|
|
929
930
|
}
|
|
@@ -1098,6 +1099,40 @@ function initBuilderSelection() {
|
|
|
1098
1099
|
}
|
|
1099
1100
|
}
|
|
1100
1101
|
|
|
1102
|
+
// src/lib/collection-context-registry.ts
|
|
1103
|
+
var registry = /* @__PURE__ */ new Map();
|
|
1104
|
+
function registerCollectionContext(prefix, context) {
|
|
1105
|
+
if (registry.has(prefix)) {
|
|
1106
|
+
console.warn(
|
|
1107
|
+
`[collection-registry] Overwriting existing context for prefix "${prefix}". This usually means multiple CollectionContentProviders with the same prefix are mounted.`
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
registry.set(prefix, context);
|
|
1111
|
+
}
|
|
1112
|
+
function unregisterCollectionContext(prefix) {
|
|
1113
|
+
registry.delete(prefix);
|
|
1114
|
+
}
|
|
1115
|
+
function getCollectionContextForField(fieldId) {
|
|
1116
|
+
for (const [prefix, context] of registry) {
|
|
1117
|
+
if (fieldId === prefix || fieldId.startsWith(prefix + ".")) {
|
|
1118
|
+
return context;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return null;
|
|
1122
|
+
}
|
|
1123
|
+
function getAllCollectionFields() {
|
|
1124
|
+
const result = [];
|
|
1125
|
+
for (const [prefix, context] of registry) {
|
|
1126
|
+
result.push({
|
|
1127
|
+
prefix,
|
|
1128
|
+
recordId: context.recordId,
|
|
1129
|
+
collectionSlug: context.collectionSlug,
|
|
1130
|
+
appId: context.appId
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
return result;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1101
1136
|
// src/components/ContentStoreProvider.tsx
|
|
1102
1137
|
import { jsx } from "react/jsx-runtime";
|
|
1103
1138
|
var ContentStoreContext = createContext(null);
|
|
@@ -1124,7 +1159,7 @@ function ContentStoreProvider({
|
|
|
1124
1159
|
const defaultContent = initialContent ?? getAllContent2();
|
|
1125
1160
|
const defaultMode = initialMode ?? (isBuilderPreview() ? "inline-edit" : "read-only");
|
|
1126
1161
|
const [content, setContent] = useState(
|
|
1127
|
-
new Map(Object.entries(defaultContent))
|
|
1162
|
+
() => new Map(Object.entries(defaultContent))
|
|
1128
1163
|
);
|
|
1129
1164
|
const [mode, setModeState] = useState(defaultMode);
|
|
1130
1165
|
const [listeners, setListeners] = useState(/* @__PURE__ */ new Set());
|
|
@@ -1264,21 +1299,101 @@ function ContentStoreProvider({
|
|
|
1264
1299
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
1265
1300
|
};
|
|
1266
1301
|
}, [activeFieldId]);
|
|
1302
|
+
const getValueRef = useRef(getValue);
|
|
1303
|
+
const setValueRef = useRef(setValue);
|
|
1304
|
+
const getChangeSourceRef = useRef(getChangeSource);
|
|
1305
|
+
const setModeRef = useRef(setMode);
|
|
1306
|
+
const subscribeRef = useRef(subscribe);
|
|
1307
|
+
const saveToWorkerRef = useRef(saveToWorker);
|
|
1308
|
+
const modeRef = useRef(mode);
|
|
1309
|
+
useEffect(() => {
|
|
1310
|
+
getValueRef.current = getValue;
|
|
1311
|
+
}, [getValue]);
|
|
1312
|
+
useEffect(() => {
|
|
1313
|
+
setValueRef.current = setValue;
|
|
1314
|
+
}, [setValue]);
|
|
1315
|
+
useEffect(() => {
|
|
1316
|
+
getChangeSourceRef.current = getChangeSource;
|
|
1317
|
+
}, [getChangeSource]);
|
|
1318
|
+
useEffect(() => {
|
|
1319
|
+
setModeRef.current = setMode;
|
|
1320
|
+
}, [setMode]);
|
|
1321
|
+
useEffect(() => {
|
|
1322
|
+
subscribeRef.current = subscribe;
|
|
1323
|
+
}, [subscribe]);
|
|
1324
|
+
useEffect(() => {
|
|
1325
|
+
saveToWorkerRef.current = saveToWorker;
|
|
1326
|
+
}, [saveToWorker]);
|
|
1327
|
+
useEffect(() => {
|
|
1328
|
+
modeRef.current = mode;
|
|
1329
|
+
}, [mode]);
|
|
1330
|
+
const contentRef = useRef(content);
|
|
1331
|
+
useEffect(() => {
|
|
1332
|
+
contentRef.current = content;
|
|
1333
|
+
}, [content]);
|
|
1267
1334
|
useEffect(() => {
|
|
1268
1335
|
;
|
|
1269
|
-
window.
|
|
1270
|
-
getValue,
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1336
|
+
window.yaContentStore = {
|
|
1337
|
+
// getValue: Check collection context first, then static content
|
|
1338
|
+
getValue: (fieldId) => {
|
|
1339
|
+
const collectionCtx = getCollectionContextForField(fieldId);
|
|
1340
|
+
if (collectionCtx) {
|
|
1341
|
+
return collectionCtx.getValue(fieldId);
|
|
1342
|
+
}
|
|
1343
|
+
return getValueRef.current(fieldId);
|
|
1344
|
+
},
|
|
1345
|
+
// setValue: Route to collection context if field belongs to one
|
|
1346
|
+
setValue: (fieldId, value2, source) => {
|
|
1347
|
+
const collectionCtx = getCollectionContextForField(fieldId);
|
|
1348
|
+
if (collectionCtx?.setValue) {
|
|
1349
|
+
collectionCtx.setValue(fieldId, value2, source);
|
|
1350
|
+
collectionCtx.saveToBackend?.(fieldId, value2);
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
setValueRef.current(fieldId, value2, source);
|
|
1354
|
+
},
|
|
1355
|
+
// getChangeSource: Check collection context first
|
|
1356
|
+
getChangeSource: (fieldId) => {
|
|
1357
|
+
return getChangeSourceRef.current(fieldId);
|
|
1358
|
+
},
|
|
1359
|
+
getMode: () => modeRef.current,
|
|
1360
|
+
setMode: (mode2) => setModeRef.current(mode2),
|
|
1361
|
+
subscribe: (listener) => subscribeRef.current(listener),
|
|
1362
|
+
saveToWorker: saveToWorkerRef.current ? (fieldId, value2) => saveToWorkerRef.current?.(fieldId, value2) : void 0,
|
|
1363
|
+
// NEW: Get info about a field (is it static or collection?)
|
|
1364
|
+
getFieldInfo: (fieldId) => {
|
|
1365
|
+
const collectionCtx = getCollectionContextForField(fieldId);
|
|
1366
|
+
if (collectionCtx) {
|
|
1367
|
+
return {
|
|
1368
|
+
isCollectionField: true,
|
|
1369
|
+
collectionSlug: collectionCtx.collectionSlug,
|
|
1370
|
+
recordId: collectionCtx.recordId,
|
|
1371
|
+
prefix: collectionCtx.prefix,
|
|
1372
|
+
appId: collectionCtx.appId
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
return {
|
|
1376
|
+
isCollectionField: false,
|
|
1377
|
+
collectionSlug: void 0,
|
|
1378
|
+
recordId: void 0,
|
|
1379
|
+
prefix: void 0,
|
|
1380
|
+
appId: void 0
|
|
1381
|
+
};
|
|
1382
|
+
},
|
|
1383
|
+
// NEW: List all editable fields (for AI discovery)
|
|
1384
|
+
listEditableFields: () => {
|
|
1385
|
+
const staticFields = Array.from(contentRef.current.keys());
|
|
1386
|
+
const collectionFields = getAllCollectionFields();
|
|
1387
|
+
return {
|
|
1388
|
+
static: staticFields,
|
|
1389
|
+
collection: collectionFields
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1277
1392
|
};
|
|
1278
1393
|
return () => {
|
|
1279
|
-
delete window.
|
|
1394
|
+
delete window.yaContentStore;
|
|
1280
1395
|
};
|
|
1281
|
-
}, [
|
|
1396
|
+
}, []);
|
|
1282
1397
|
const getPages = useCallback(() => pages, [pages]);
|
|
1283
1398
|
const value = {
|
|
1284
1399
|
getValue,
|
|
@@ -1361,7 +1476,7 @@ function ContentStoreProvider2({ children, initialContent }) {
|
|
|
1361
1476
|
}
|
|
1362
1477
|
|
|
1363
1478
|
// src/components/YaText.tsx
|
|
1364
|
-
import { useEffect as
|
|
1479
|
+
import { useEffect as useEffect8, useRef as useRef9, useState as useState8, useCallback as useCallback10 } from "react";
|
|
1365
1480
|
import { createPortal as createPortal4 } from "react-dom";
|
|
1366
1481
|
import { useEditor, EditorContent } from "@tiptap/react";
|
|
1367
1482
|
|
|
@@ -1471,11 +1586,322 @@ import Link from "@tiptap/extension-link";
|
|
|
1471
1586
|
import { TextStyle } from "@tiptap/extension-text-style";
|
|
1472
1587
|
import { Extension } from "@tiptap/core";
|
|
1473
1588
|
|
|
1589
|
+
// src/hooks/useContent.ts
|
|
1590
|
+
import { useMemo as useMemo3 } from "react";
|
|
1591
|
+
|
|
1592
|
+
// src/components/CollectionContentProvider.tsx
|
|
1593
|
+
import {
|
|
1594
|
+
createContext as createContext3,
|
|
1595
|
+
useCallback as useCallback3,
|
|
1596
|
+
useContext as useContext3,
|
|
1597
|
+
useEffect as useEffect3,
|
|
1598
|
+
useMemo as useMemo2,
|
|
1599
|
+
useRef as useRef3,
|
|
1600
|
+
useState as useState3
|
|
1601
|
+
} from "react";
|
|
1602
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1603
|
+
var CollectionContentContext = createContext3(null);
|
|
1604
|
+
function useCollectionContent() {
|
|
1605
|
+
return useContext3(CollectionContentContext);
|
|
1606
|
+
}
|
|
1607
|
+
function extractSessionId2() {
|
|
1608
|
+
if (typeof window === "undefined") return null;
|
|
1609
|
+
const match = window.location.pathname.match(/\/session\/([^/]+)/);
|
|
1610
|
+
return match ? match[1] : null;
|
|
1611
|
+
}
|
|
1612
|
+
function isBuilderPreview2() {
|
|
1613
|
+
return typeof window !== "undefined" && (window.__YOAMIGO_EDIT_MODE__ === true || typeof import.meta !== "undefined" && import.meta.env?.YA_EDIT_MODE === "true");
|
|
1614
|
+
}
|
|
1615
|
+
function flattenObject(obj, prefix) {
|
|
1616
|
+
const result = {};
|
|
1617
|
+
function flatten(current, currentPath) {
|
|
1618
|
+
if (current === null || current === void 0) {
|
|
1619
|
+
result[currentPath] = "";
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1622
|
+
if (typeof current === "string") {
|
|
1623
|
+
result[currentPath] = current;
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
if (typeof current === "number" || typeof current === "boolean") {
|
|
1627
|
+
result[currentPath] = String(current);
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
if (Array.isArray(current)) {
|
|
1631
|
+
result[currentPath] = JSON.stringify(current);
|
|
1632
|
+
current.forEach((item, index) => {
|
|
1633
|
+
flatten(item, `${currentPath}.${index}`);
|
|
1634
|
+
});
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
if (typeof current === "object") {
|
|
1638
|
+
const record = current;
|
|
1639
|
+
if (record.src && typeof record.src === "string") {
|
|
1640
|
+
result[currentPath] = JSON.stringify(current);
|
|
1641
|
+
}
|
|
1642
|
+
for (const [key, value] of Object.entries(record)) {
|
|
1643
|
+
flatten(value, `${currentPath}.${key}`);
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1648
|
+
flatten(value, `${prefix}.${key}`);
|
|
1649
|
+
}
|
|
1650
|
+
return result;
|
|
1651
|
+
}
|
|
1652
|
+
function CollectionContentProvider({
|
|
1653
|
+
record,
|
|
1654
|
+
prefix,
|
|
1655
|
+
children,
|
|
1656
|
+
recordId,
|
|
1657
|
+
collectionSlug,
|
|
1658
|
+
appId,
|
|
1659
|
+
// changeType is auto-determined from recordId presence
|
|
1660
|
+
changeType: _changeType = "UPDATE"
|
|
1661
|
+
}) {
|
|
1662
|
+
const isEditable = Boolean(
|
|
1663
|
+
collectionSlug && appId && isBuilderPreview2()
|
|
1664
|
+
);
|
|
1665
|
+
const baseContentMap = useMemo2(() => {
|
|
1666
|
+
return flattenObject(record, prefix);
|
|
1667
|
+
}, [record, prefix]);
|
|
1668
|
+
const [localOverrides, setLocalOverrides] = useState3({});
|
|
1669
|
+
const [changeSourceMap, setChangeSourceMap] = useState3({});
|
|
1670
|
+
const isMountedRef = useRef3(true);
|
|
1671
|
+
useEffect3(() => {
|
|
1672
|
+
isMountedRef.current = true;
|
|
1673
|
+
return () => {
|
|
1674
|
+
isMountedRef.current = false;
|
|
1675
|
+
};
|
|
1676
|
+
}, []);
|
|
1677
|
+
const getValue = useCallback3(
|
|
1678
|
+
(fieldId) => {
|
|
1679
|
+
if (fieldId in localOverrides) {
|
|
1680
|
+
return localOverrides[fieldId];
|
|
1681
|
+
}
|
|
1682
|
+
if (fieldId in baseContentMap) {
|
|
1683
|
+
return baseContentMap[fieldId];
|
|
1684
|
+
}
|
|
1685
|
+
const prefixedFieldId = fieldId.startsWith(prefix + ".") ? fieldId : `${prefix}.${fieldId}`;
|
|
1686
|
+
if (prefixedFieldId in localOverrides) {
|
|
1687
|
+
return localOverrides[prefixedFieldId];
|
|
1688
|
+
}
|
|
1689
|
+
if (prefixedFieldId in baseContentMap) {
|
|
1690
|
+
return baseContentMap[prefixedFieldId];
|
|
1691
|
+
}
|
|
1692
|
+
return "";
|
|
1693
|
+
},
|
|
1694
|
+
[baseContentMap, localOverrides, prefix]
|
|
1695
|
+
);
|
|
1696
|
+
const setValue = useCallback3(
|
|
1697
|
+
(fieldId, value, source = "ai") => {
|
|
1698
|
+
if (!isEditable) {
|
|
1699
|
+
console.warn("[CollectionContentProvider] setValue called but editing is not enabled");
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
setLocalOverrides((prev) => ({ ...prev, [fieldId]: value }));
|
|
1703
|
+
setChangeSourceMap((prev) => ({ ...prev, [fieldId]: source }));
|
|
1704
|
+
},
|
|
1705
|
+
[isEditable]
|
|
1706
|
+
);
|
|
1707
|
+
const getChangeSource = useCallback3(
|
|
1708
|
+
(fieldId) => changeSourceMap[fieldId] || "initial",
|
|
1709
|
+
[changeSourceMap]
|
|
1710
|
+
);
|
|
1711
|
+
const clearChangeSource = useCallback3(
|
|
1712
|
+
(fieldId) => {
|
|
1713
|
+
setChangeSourceMap((prev) => {
|
|
1714
|
+
const next = { ...prev };
|
|
1715
|
+
delete next[fieldId];
|
|
1716
|
+
return next;
|
|
1717
|
+
});
|
|
1718
|
+
},
|
|
1719
|
+
[]
|
|
1720
|
+
);
|
|
1721
|
+
const saveToBackend = useCallback3(
|
|
1722
|
+
async (fieldId, value) => {
|
|
1723
|
+
if (!isEditable) {
|
|
1724
|
+
console.warn("[CollectionContentProvider] saveToBackend called but editing is not enabled");
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
const sessionId = extractSessionId2();
|
|
1728
|
+
if (!sessionId) {
|
|
1729
|
+
console.warn("[CollectionContentProvider] saveToBackend: Not in builder preview mode, skipping");
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
try {
|
|
1733
|
+
const response = await fetch(`/api/session/${sessionId}/collection-edit`, {
|
|
1734
|
+
method: "POST",
|
|
1735
|
+
headers: { "Content-Type": "application/json" },
|
|
1736
|
+
credentials: "include",
|
|
1737
|
+
body: JSON.stringify({
|
|
1738
|
+
fieldId,
|
|
1739
|
+
value,
|
|
1740
|
+
recordId,
|
|
1741
|
+
collectionSlug,
|
|
1742
|
+
appId,
|
|
1743
|
+
changeType: recordId ? "UPDATE" : "CREATE"
|
|
1744
|
+
})
|
|
1745
|
+
});
|
|
1746
|
+
if (!response.ok) {
|
|
1747
|
+
const error = await response.json();
|
|
1748
|
+
console.error("[CollectionContentProvider] saveToBackend failed:", error);
|
|
1749
|
+
throw new Error(error.error || "Failed to save collection edit");
|
|
1750
|
+
}
|
|
1751
|
+
console.log(`[CollectionContentProvider] Saved ${fieldId} to staging`);
|
|
1752
|
+
} catch (error) {
|
|
1753
|
+
console.error("[CollectionContentProvider] saveToBackend error:", error);
|
|
1754
|
+
throw error;
|
|
1755
|
+
}
|
|
1756
|
+
},
|
|
1757
|
+
[isEditable, recordId, collectionSlug, appId]
|
|
1758
|
+
);
|
|
1759
|
+
const contextValue = useMemo2(() => ({
|
|
1760
|
+
getValue,
|
|
1761
|
+
setValue: isEditable ? setValue : void 0,
|
|
1762
|
+
saveToBackend: isEditable ? saveToBackend : void 0,
|
|
1763
|
+
getChangeSource: isEditable ? getChangeSource : void 0,
|
|
1764
|
+
clearChangeSource: isEditable ? clearChangeSource : void 0,
|
|
1765
|
+
isCollectionContent: true,
|
|
1766
|
+
prefix,
|
|
1767
|
+
recordId,
|
|
1768
|
+
collectionSlug,
|
|
1769
|
+
appId,
|
|
1770
|
+
isEditable
|
|
1771
|
+
}), [
|
|
1772
|
+
getValue,
|
|
1773
|
+
setValue,
|
|
1774
|
+
saveToBackend,
|
|
1775
|
+
getChangeSource,
|
|
1776
|
+
clearChangeSource,
|
|
1777
|
+
isEditable,
|
|
1778
|
+
prefix,
|
|
1779
|
+
recordId,
|
|
1780
|
+
collectionSlug,
|
|
1781
|
+
appId
|
|
1782
|
+
]);
|
|
1783
|
+
const contextValueRef = useRef3(contextValue);
|
|
1784
|
+
useEffect3(() => {
|
|
1785
|
+
contextValueRef.current = contextValue;
|
|
1786
|
+
}, [contextValue]);
|
|
1787
|
+
useEffect3(() => {
|
|
1788
|
+
if (!isEditable) return;
|
|
1789
|
+
const registryContext = {
|
|
1790
|
+
getValue: (fid) => contextValueRef.current.getValue(fid),
|
|
1791
|
+
setValue: (fid, val, src) => {
|
|
1792
|
+
contextValueRef.current.setValue?.(fid, val, src);
|
|
1793
|
+
},
|
|
1794
|
+
saveToBackend: (fid, val) => {
|
|
1795
|
+
return contextValueRef.current.saveToBackend?.(fid, val) ?? Promise.resolve();
|
|
1796
|
+
},
|
|
1797
|
+
prefix,
|
|
1798
|
+
recordId,
|
|
1799
|
+
collectionSlug,
|
|
1800
|
+
appId
|
|
1801
|
+
};
|
|
1802
|
+
registerCollectionContext(prefix, registryContext);
|
|
1803
|
+
return () => {
|
|
1804
|
+
unregisterCollectionContext(prefix);
|
|
1805
|
+
};
|
|
1806
|
+
}, [isEditable, prefix, recordId, collectionSlug, appId]);
|
|
1807
|
+
return /* @__PURE__ */ jsx4(CollectionContentContext.Provider, { value: contextValue, children });
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// src/types/fields.ts
|
|
1811
|
+
function parseFieldValue(raw) {
|
|
1812
|
+
if (!raw) {
|
|
1813
|
+
return {};
|
|
1814
|
+
}
|
|
1815
|
+
if (typeof raw === "string") {
|
|
1816
|
+
if (raw.startsWith("{") || raw.startsWith("[")) {
|
|
1817
|
+
try {
|
|
1818
|
+
return JSON.parse(raw);
|
|
1819
|
+
} catch {
|
|
1820
|
+
return raw;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
return raw;
|
|
1824
|
+
}
|
|
1825
|
+
return raw;
|
|
1826
|
+
}
|
|
1827
|
+
function stringifyFieldValue(value) {
|
|
1828
|
+
if (typeof value === "string") {
|
|
1829
|
+
return value;
|
|
1830
|
+
}
|
|
1831
|
+
return JSON.stringify(value);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
// src/hooks/useContent.ts
|
|
1835
|
+
function useContent(fieldId) {
|
|
1836
|
+
const contentStore = useContentStore();
|
|
1837
|
+
const collectionContext = useCollectionContent();
|
|
1838
|
+
const isInCollection = Boolean(
|
|
1839
|
+
collectionContext?.isEditable && fieldId.startsWith(collectionContext.prefix + ".")
|
|
1840
|
+
);
|
|
1841
|
+
return useMemo3(() => {
|
|
1842
|
+
const getValueFn = () => {
|
|
1843
|
+
return isInCollection ? collectionContext.getValue(fieldId) : contentStore.getValue(fieldId);
|
|
1844
|
+
};
|
|
1845
|
+
const setValueFn = (value, source) => {
|
|
1846
|
+
if (isInCollection && collectionContext.setValue) {
|
|
1847
|
+
collectionContext.setValue(fieldId, value, source);
|
|
1848
|
+
} else {
|
|
1849
|
+
contentStore.setValue(fieldId, value, source);
|
|
1850
|
+
}
|
|
1851
|
+
};
|
|
1852
|
+
const saveFn = async () => {
|
|
1853
|
+
const value = getValueFn();
|
|
1854
|
+
if (isInCollection && collectionContext.saveToBackend) {
|
|
1855
|
+
await collectionContext.saveToBackend(fieldId, value);
|
|
1856
|
+
} else if (contentStore.saveToWorker) {
|
|
1857
|
+
await contentStore.saveToWorker(fieldId, value);
|
|
1858
|
+
}
|
|
1859
|
+
};
|
|
1860
|
+
const getChangeSourceFn = () => {
|
|
1861
|
+
if (isInCollection) {
|
|
1862
|
+
return collectionContext.getChangeSource?.(fieldId);
|
|
1863
|
+
}
|
|
1864
|
+
return contentStore.getChangeSource?.(fieldId);
|
|
1865
|
+
};
|
|
1866
|
+
const clearChangeSourceFn = () => {
|
|
1867
|
+
if (isInCollection) {
|
|
1868
|
+
collectionContext.clearChangeSource?.(fieldId);
|
|
1869
|
+
} else {
|
|
1870
|
+
contentStore.clearChangeSource?.(fieldId);
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
return {
|
|
1874
|
+
get value() {
|
|
1875
|
+
const raw = getValueFn();
|
|
1876
|
+
return parseFieldValue(raw);
|
|
1877
|
+
},
|
|
1878
|
+
get() {
|
|
1879
|
+
return getValueFn();
|
|
1880
|
+
},
|
|
1881
|
+
set(value, source) {
|
|
1882
|
+
const stringValue = typeof value === "string" ? value : stringifyFieldValue(value);
|
|
1883
|
+
setValueFn(stringValue, source);
|
|
1884
|
+
},
|
|
1885
|
+
async save() {
|
|
1886
|
+
await saveFn();
|
|
1887
|
+
},
|
|
1888
|
+
get changeSource() {
|
|
1889
|
+
return getChangeSourceFn();
|
|
1890
|
+
},
|
|
1891
|
+
clearChangeSource() {
|
|
1892
|
+
clearChangeSourceFn();
|
|
1893
|
+
},
|
|
1894
|
+
mode: contentStore.mode,
|
|
1895
|
+
isCollectionField: isInCollection
|
|
1896
|
+
};
|
|
1897
|
+
}, [fieldId, isInCollection, collectionContext, contentStore]);
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1474
1900
|
// src/components/SafeHtml.tsx
|
|
1475
|
-
import { useEffect as
|
|
1901
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState4, useCallback as useCallback4 } from "react";
|
|
1476
1902
|
import { createPortal as createPortal2 } from "react-dom";
|
|
1477
1903
|
import DOMPurify from "dompurify";
|
|
1478
|
-
import { Fragment, jsx as
|
|
1904
|
+
import { Fragment, jsx as jsx5, jsxs } from "react/jsx-runtime";
|
|
1479
1905
|
var ALLOWED_TAGS = ["strong", "em", "a", "span", "br", "b", "i", "u"];
|
|
1480
1906
|
var ALLOWED_ATTR = ["href", "style", "class", "target", "rel"];
|
|
1481
1907
|
if (typeof window !== "undefined") {
|
|
@@ -1533,8 +1959,8 @@ function LinkIcon() {
|
|
|
1533
1959
|
strokeLinecap: "round",
|
|
1534
1960
|
strokeLinejoin: "round",
|
|
1535
1961
|
children: [
|
|
1536
|
-
/* @__PURE__ */
|
|
1537
|
-
/* @__PURE__ */
|
|
1962
|
+
/* @__PURE__ */ jsx5("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
1963
|
+
/* @__PURE__ */ jsx5("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
1538
1964
|
]
|
|
1539
1965
|
}
|
|
1540
1966
|
);
|
|
@@ -1566,20 +1992,20 @@ function LinkPopover({
|
|
|
1566
1992
|
}
|
|
1567
1993
|
},
|
|
1568
1994
|
children: [
|
|
1569
|
-
/* @__PURE__ */
|
|
1995
|
+
/* @__PURE__ */ jsx5(LinkIcon, {}),
|
|
1570
1996
|
/* @__PURE__ */ jsxs("span", { className: "ya-link-popover-text", children: [
|
|
1571
|
-
/* @__PURE__ */
|
|
1572
|
-
/* @__PURE__ */
|
|
1997
|
+
/* @__PURE__ */ jsx5("span", { className: "ya-link-popover-prefix", children: "Go to " }),
|
|
1998
|
+
/* @__PURE__ */ jsx5("span", { className: "ya-link-popover-name", children: displayText })
|
|
1573
1999
|
] })
|
|
1574
2000
|
]
|
|
1575
2001
|
}
|
|
1576
2002
|
);
|
|
1577
2003
|
}
|
|
1578
2004
|
function SafeHtml({ content, className, mode = "read-only" }) {
|
|
1579
|
-
const containerRef =
|
|
1580
|
-
const showTimerRef =
|
|
1581
|
-
const hideTimerRef =
|
|
1582
|
-
const [popoverState, setPopoverState] =
|
|
2005
|
+
const containerRef = useRef4(null);
|
|
2006
|
+
const showTimerRef = useRef4(void 0);
|
|
2007
|
+
const hideTimerRef = useRef4(void 0);
|
|
2008
|
+
const [popoverState, setPopoverState] = useState4({
|
|
1583
2009
|
isVisible: false,
|
|
1584
2010
|
href: "",
|
|
1585
2011
|
displayText: "",
|
|
@@ -1590,20 +2016,20 @@ function SafeHtml({ content, className, mode = "read-only" }) {
|
|
|
1590
2016
|
ALLOWED_TAGS,
|
|
1591
2017
|
ALLOWED_ATTR
|
|
1592
2018
|
});
|
|
1593
|
-
const hidePopover =
|
|
2019
|
+
const hidePopover = useCallback4(() => {
|
|
1594
2020
|
clearTimeout(showTimerRef.current);
|
|
1595
2021
|
setPopoverState((prev) => ({ ...prev, isVisible: false }));
|
|
1596
2022
|
}, []);
|
|
1597
|
-
const scheduleHide =
|
|
2023
|
+
const scheduleHide = useCallback4(() => {
|
|
1598
2024
|
clearTimeout(showTimerRef.current);
|
|
1599
2025
|
hideTimerRef.current = window.setTimeout(() => {
|
|
1600
2026
|
hidePopover();
|
|
1601
2027
|
}, 100);
|
|
1602
2028
|
}, [hidePopover]);
|
|
1603
|
-
const cancelHide =
|
|
2029
|
+
const cancelHide = useCallback4(() => {
|
|
1604
2030
|
clearTimeout(hideTimerRef.current);
|
|
1605
2031
|
}, []);
|
|
1606
|
-
const handlePopoverClick =
|
|
2032
|
+
const handlePopoverClick = useCallback4(() => {
|
|
1607
2033
|
if (popoverState.isExternal) {
|
|
1608
2034
|
window.open(popoverState.href, "_blank", "noopener,noreferrer");
|
|
1609
2035
|
} else {
|
|
@@ -1611,7 +2037,7 @@ function SafeHtml({ content, className, mode = "read-only" }) {
|
|
|
1611
2037
|
}
|
|
1612
2038
|
hidePopover();
|
|
1613
2039
|
}, [popoverState.href, popoverState.isExternal, hidePopover]);
|
|
1614
|
-
|
|
2040
|
+
useEffect4(() => {
|
|
1615
2041
|
if (mode !== "inline-edit" || !containerRef.current) return;
|
|
1616
2042
|
const container = containerRef.current;
|
|
1617
2043
|
const handleMouseOver = (e) => {
|
|
@@ -1669,7 +2095,7 @@ function SafeHtml({ content, className, mode = "read-only" }) {
|
|
|
1669
2095
|
};
|
|
1670
2096
|
}, [mode, scheduleHide]);
|
|
1671
2097
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1672
|
-
/* @__PURE__ */
|
|
2098
|
+
/* @__PURE__ */ jsx5(
|
|
1673
2099
|
"span",
|
|
1674
2100
|
{
|
|
1675
2101
|
ref: containerRef,
|
|
@@ -1678,7 +2104,7 @@ function SafeHtml({ content, className, mode = "read-only" }) {
|
|
|
1678
2104
|
}
|
|
1679
2105
|
),
|
|
1680
2106
|
mode === "inline-edit" && popoverState.isVisible && createPortal2(
|
|
1681
|
-
/* @__PURE__ */
|
|
2107
|
+
/* @__PURE__ */ jsx5(
|
|
1682
2108
|
LinkPopover,
|
|
1683
2109
|
{
|
|
1684
2110
|
displayText: popoverState.displayText,
|
|
@@ -1694,37 +2120,37 @@ function SafeHtml({ content, className, mode = "read-only" }) {
|
|
|
1694
2120
|
}
|
|
1695
2121
|
|
|
1696
2122
|
// src/hooks/useAnimatedText.ts
|
|
1697
|
-
import { useMemo as
|
|
2123
|
+
import { useMemo as useMemo6 } from "react";
|
|
1698
2124
|
|
|
1699
2125
|
// src/hooks/useAIEditAnimation.ts
|
|
1700
|
-
import { useState as
|
|
2126
|
+
import { useState as useState5, useEffect as useEffect5, useRef as useRef6, useCallback as useCallback6, useMemo as useMemo5 } from "react";
|
|
1701
2127
|
|
|
1702
2128
|
// src/contexts/AIEditContext.tsx
|
|
1703
|
-
import { createContext as
|
|
1704
|
-
import { jsx as
|
|
1705
|
-
var AIEditContext =
|
|
2129
|
+
import { createContext as createContext4, useContext as useContext4, useCallback as useCallback5, useRef as useRef5, useMemo as useMemo4 } from "react";
|
|
2130
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
2131
|
+
var AIEditContext = createContext4(null);
|
|
1706
2132
|
function useAIEditContext() {
|
|
1707
|
-
const context =
|
|
2133
|
+
const context = useContext4(AIEditContext);
|
|
1708
2134
|
if (!context) {
|
|
1709
2135
|
throw new Error("useAIEditContext must be used within an AIEditProvider");
|
|
1710
2136
|
}
|
|
1711
2137
|
return context;
|
|
1712
2138
|
}
|
|
1713
2139
|
function useAIEditContextOptional() {
|
|
1714
|
-
return
|
|
2140
|
+
return useContext4(AIEditContext);
|
|
1715
2141
|
}
|
|
1716
2142
|
function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
1717
|
-
const animationsRef =
|
|
1718
|
-
const listenersRef =
|
|
1719
|
-
const queueRef =
|
|
1720
|
-
const processingRef =
|
|
1721
|
-
const notifyListeners =
|
|
2143
|
+
const animationsRef = useRef5(/* @__PURE__ */ new Map());
|
|
2144
|
+
const listenersRef = useRef5(/* @__PURE__ */ new Map());
|
|
2145
|
+
const queueRef = useRef5([]);
|
|
2146
|
+
const processingRef = useRef5(false);
|
|
2147
|
+
const notifyListeners = useCallback5((fieldId) => {
|
|
1722
2148
|
const listeners = listenersRef.current.get(fieldId);
|
|
1723
2149
|
if (listeners) {
|
|
1724
2150
|
listeners.forEach((listener) => listener());
|
|
1725
2151
|
}
|
|
1726
2152
|
}, []);
|
|
1727
|
-
const processQueue =
|
|
2153
|
+
const processQueue = useCallback5(() => {
|
|
1728
2154
|
if (processingRef.current || queueRef.current.length === 0) return;
|
|
1729
2155
|
processingRef.current = true;
|
|
1730
2156
|
const fieldId = queueRef.current.shift();
|
|
@@ -1739,7 +2165,7 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1739
2165
|
processQueue();
|
|
1740
2166
|
}, staggerDelay);
|
|
1741
2167
|
}, [staggerDelay, notifyListeners]);
|
|
1742
|
-
const queueAnimation =
|
|
2168
|
+
const queueAnimation = useCallback5(
|
|
1743
2169
|
(fieldId, config) => {
|
|
1744
2170
|
const existing = animationsRef.current.get(fieldId);
|
|
1745
2171
|
if (existing?.status === "animating") {
|
|
@@ -1762,7 +2188,7 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1762
2188
|
},
|
|
1763
2189
|
[notifyListeners, processQueue]
|
|
1764
2190
|
);
|
|
1765
|
-
const cancelAnimation =
|
|
2191
|
+
const cancelAnimation = useCallback5(
|
|
1766
2192
|
(fieldId) => {
|
|
1767
2193
|
const state = animationsRef.current.get(fieldId);
|
|
1768
2194
|
if (state) {
|
|
@@ -1776,14 +2202,14 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1776
2202
|
},
|
|
1777
2203
|
[notifyListeners]
|
|
1778
2204
|
);
|
|
1779
|
-
const isAnimating =
|
|
2205
|
+
const isAnimating = useCallback5((fieldId) => {
|
|
1780
2206
|
const state = animationsRef.current.get(fieldId);
|
|
1781
2207
|
return state?.status === "animating" || state?.status === "pending";
|
|
1782
2208
|
}, []);
|
|
1783
|
-
const getAnimationState =
|
|
2209
|
+
const getAnimationState = useCallback5((fieldId) => {
|
|
1784
2210
|
return animationsRef.current.get(fieldId);
|
|
1785
2211
|
}, []);
|
|
1786
|
-
const subscribe =
|
|
2212
|
+
const subscribe = useCallback5((fieldId, listener) => {
|
|
1787
2213
|
let listeners = listenersRef.current.get(fieldId);
|
|
1788
2214
|
if (!listeners) {
|
|
1789
2215
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -1797,7 +2223,7 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1797
2223
|
}
|
|
1798
2224
|
};
|
|
1799
2225
|
}, []);
|
|
1800
|
-
const completeAnimation =
|
|
2226
|
+
const completeAnimation = useCallback5(
|
|
1801
2227
|
(fieldId) => {
|
|
1802
2228
|
const state = animationsRef.current.get(fieldId);
|
|
1803
2229
|
if (state) {
|
|
@@ -1812,7 +2238,7 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1812
2238
|
},
|
|
1813
2239
|
[notifyListeners]
|
|
1814
2240
|
);
|
|
1815
|
-
const value =
|
|
2241
|
+
const value = useMemo4(
|
|
1816
2242
|
() => ({
|
|
1817
2243
|
queueAnimation,
|
|
1818
2244
|
cancelAnimation,
|
|
@@ -1823,7 +2249,7 @@ function AIEditProvider({ children, staggerDelay = 100 }) {
|
|
|
1823
2249
|
}),
|
|
1824
2250
|
[queueAnimation, cancelAnimation, isAnimating, getAnimationState, subscribe, completeAnimation]
|
|
1825
2251
|
);
|
|
1826
|
-
return /* @__PURE__ */
|
|
2252
|
+
return /* @__PURE__ */ jsx6(AIEditContext.Provider, { value, children });
|
|
1827
2253
|
}
|
|
1828
2254
|
|
|
1829
2255
|
// src/hooks/useAIEditAnimation.ts
|
|
@@ -1831,15 +2257,15 @@ function useAIEditAnimation(fieldId, value, options) {
|
|
|
1831
2257
|
const { enabled = true, strategy, maxDuration = 2e3, onStart, onComplete } = options;
|
|
1832
2258
|
const context = useAIEditContextOptional();
|
|
1833
2259
|
const { getChangeSource, clearChangeSource } = useContentStore();
|
|
1834
|
-
const previousValueRef =
|
|
1835
|
-
const isFirstRender =
|
|
1836
|
-
const [phase, setPhase] =
|
|
1837
|
-
const [progress, setProgress] =
|
|
1838
|
-
const [displayValue, setDisplayValue] =
|
|
1839
|
-
const metadataRef =
|
|
1840
|
-
const animationFrameRef =
|
|
1841
|
-
const startTimeRef =
|
|
1842
|
-
const cancel =
|
|
2260
|
+
const previousValueRef = useRef6(value);
|
|
2261
|
+
const isFirstRender = useRef6(true);
|
|
2262
|
+
const [phase, setPhase] = useState5("idle");
|
|
2263
|
+
const [progress, setProgress] = useState5(0);
|
|
2264
|
+
const [displayValue, setDisplayValue] = useState5(value);
|
|
2265
|
+
const metadataRef = useRef6(null);
|
|
2266
|
+
const animationFrameRef = useRef6(null);
|
|
2267
|
+
const startTimeRef = useRef6(0);
|
|
2268
|
+
const cancel = useCallback6(() => {
|
|
1843
2269
|
if (animationFrameRef.current !== null) {
|
|
1844
2270
|
cancelAnimationFrame(animationFrameRef.current);
|
|
1845
2271
|
animationFrameRef.current = null;
|
|
@@ -1850,7 +2276,7 @@ function useAIEditAnimation(fieldId, value, options) {
|
|
|
1850
2276
|
metadataRef.current = null;
|
|
1851
2277
|
context?.cancelAnimation(fieldId);
|
|
1852
2278
|
}, [value, context, fieldId]);
|
|
1853
|
-
const runAnimation =
|
|
2279
|
+
const runAnimation = useCallback6(() => {
|
|
1854
2280
|
if (!metadataRef.current) return;
|
|
1855
2281
|
const metadata = metadataRef.current;
|
|
1856
2282
|
const elapsed = performance.now() - startTimeRef.current;
|
|
@@ -1875,7 +2301,7 @@ function useAIEditAnimation(fieldId, value, options) {
|
|
|
1875
2301
|
setDisplayValue(interpolatedValue);
|
|
1876
2302
|
animationFrameRef.current = requestAnimationFrame(runAnimation);
|
|
1877
2303
|
}, [strategy, maxDuration, context, fieldId, onComplete, clearChangeSource]);
|
|
1878
|
-
|
|
2304
|
+
useEffect5(() => {
|
|
1879
2305
|
if (isFirstRender.current) {
|
|
1880
2306
|
isFirstRender.current = false;
|
|
1881
2307
|
previousValueRef.current = value;
|
|
@@ -1902,6 +2328,9 @@ function useAIEditAnimation(fieldId, value, options) {
|
|
|
1902
2328
|
if (!strategy.canAnimate(oldValue, newValue)) {
|
|
1903
2329
|
setDisplayValue(newValue);
|
|
1904
2330
|
previousValueRef.current = newValue;
|
|
2331
|
+
if (phase === "animating") {
|
|
2332
|
+
setPhase("idle");
|
|
2333
|
+
}
|
|
1905
2334
|
return;
|
|
1906
2335
|
}
|
|
1907
2336
|
if (animationFrameRef.current !== null) {
|
|
@@ -1926,14 +2355,14 @@ function useAIEditAnimation(fieldId, value, options) {
|
|
|
1926
2355
|
}
|
|
1927
2356
|
};
|
|
1928
2357
|
}, [value, enabled, strategy, context, fieldId, maxDuration, onStart, runAnimation, getChangeSource, clearChangeSource]);
|
|
1929
|
-
|
|
2358
|
+
useEffect5(() => {
|
|
1930
2359
|
return () => {
|
|
1931
2360
|
if (animationFrameRef.current !== null) {
|
|
1932
2361
|
cancelAnimationFrame(animationFrameRef.current);
|
|
1933
2362
|
}
|
|
1934
2363
|
};
|
|
1935
2364
|
}, []);
|
|
1936
|
-
const wrapperProps =
|
|
2365
|
+
const wrapperProps = useMemo5(
|
|
1937
2366
|
() => ({
|
|
1938
2367
|
className: phase === "animating" ? "ya-ai-editing" : phase === "complete" ? "ya-ai-complete" : "",
|
|
1939
2368
|
"data-ai-editing": phase === "animating"
|
|
@@ -2155,7 +2584,7 @@ function useAnimatedText(fieldId, content, options = {}) {
|
|
|
2155
2584
|
onStart,
|
|
2156
2585
|
onComplete
|
|
2157
2586
|
} = options;
|
|
2158
|
-
const customStrategy =
|
|
2587
|
+
const customStrategy = useMemo6(() => {
|
|
2159
2588
|
if (charDelay === 50) {
|
|
2160
2589
|
return textTypingStrategy;
|
|
2161
2590
|
}
|
|
@@ -2194,7 +2623,7 @@ function useAnimatedText(fieldId, content, options = {}) {
|
|
|
2194
2623
|
onStart,
|
|
2195
2624
|
onComplete
|
|
2196
2625
|
});
|
|
2197
|
-
const cursorPosition =
|
|
2626
|
+
const cursorPosition = useMemo6(() => {
|
|
2198
2627
|
if (!isAnimating) return null;
|
|
2199
2628
|
const diff = computeTextDiff(content, displayValue);
|
|
2200
2629
|
const { deleteDuration, typeDuration } = calculateAnimationTiming(diff, {
|
|
@@ -2225,7 +2654,7 @@ function useAnimatedText(fieldId, content, options = {}) {
|
|
|
2225
2654
|
}
|
|
2226
2655
|
|
|
2227
2656
|
// src/components/BubbleIcons.tsx
|
|
2228
|
-
import { jsx as
|
|
2657
|
+
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2229
2658
|
function BoldIcon({ size = 16, className }) {
|
|
2230
2659
|
return /* @__PURE__ */ jsxs2(
|
|
2231
2660
|
"svg",
|
|
@@ -2240,8 +2669,8 @@ function BoldIcon({ size = 16, className }) {
|
|
|
2240
2669
|
strokeLinejoin: "round",
|
|
2241
2670
|
className,
|
|
2242
2671
|
children: [
|
|
2243
|
-
/* @__PURE__ */
|
|
2244
|
-
/* @__PURE__ */
|
|
2672
|
+
/* @__PURE__ */ jsx7("path", { d: "M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" }),
|
|
2673
|
+
/* @__PURE__ */ jsx7("path", { d: "M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" })
|
|
2245
2674
|
]
|
|
2246
2675
|
}
|
|
2247
2676
|
);
|
|
@@ -2260,9 +2689,9 @@ function ItalicIcon({ size = 16, className }) {
|
|
|
2260
2689
|
strokeLinejoin: "round",
|
|
2261
2690
|
className,
|
|
2262
2691
|
children: [
|
|
2263
|
-
/* @__PURE__ */
|
|
2264
|
-
/* @__PURE__ */
|
|
2265
|
-
/* @__PURE__ */
|
|
2692
|
+
/* @__PURE__ */ jsx7("line", { x1: "19", y1: "4", x2: "10", y2: "4" }),
|
|
2693
|
+
/* @__PURE__ */ jsx7("line", { x1: "14", y1: "20", x2: "5", y2: "20" }),
|
|
2694
|
+
/* @__PURE__ */ jsx7("line", { x1: "15", y1: "4", x2: "9", y2: "20" })
|
|
2266
2695
|
]
|
|
2267
2696
|
}
|
|
2268
2697
|
);
|
|
@@ -2281,14 +2710,14 @@ function LinkIcon2({ size = 16, className }) {
|
|
|
2281
2710
|
strokeLinejoin: "round",
|
|
2282
2711
|
className,
|
|
2283
2712
|
children: [
|
|
2284
|
-
/* @__PURE__ */
|
|
2285
|
-
/* @__PURE__ */
|
|
2713
|
+
/* @__PURE__ */ jsx7("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
2714
|
+
/* @__PURE__ */ jsx7("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
2286
2715
|
]
|
|
2287
2716
|
}
|
|
2288
2717
|
);
|
|
2289
2718
|
}
|
|
2290
2719
|
function ChevronDownIcon({ size = 12, className }) {
|
|
2291
|
-
return /* @__PURE__ */
|
|
2720
|
+
return /* @__PURE__ */ jsx7(
|
|
2292
2721
|
"svg",
|
|
2293
2722
|
{
|
|
2294
2723
|
width: size,
|
|
@@ -2300,15 +2729,15 @@ function ChevronDownIcon({ size = 12, className }) {
|
|
|
2300
2729
|
strokeLinecap: "round",
|
|
2301
2730
|
strokeLinejoin: "round",
|
|
2302
2731
|
className,
|
|
2303
|
-
children: /* @__PURE__ */
|
|
2732
|
+
children: /* @__PURE__ */ jsx7("polyline", { points: "6 9 12 15 18 9" })
|
|
2304
2733
|
}
|
|
2305
2734
|
);
|
|
2306
2735
|
}
|
|
2307
2736
|
|
|
2308
2737
|
// src/components/BubbleDropdown.tsx
|
|
2309
|
-
import { useEffect as
|
|
2738
|
+
import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useState as useState6, useRef as useRef7, useCallback as useCallback7 } from "react";
|
|
2310
2739
|
import { createPortal as createPortal3 } from "react-dom";
|
|
2311
|
-
import { Fragment as Fragment2, jsx as
|
|
2740
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2312
2741
|
function BubbleDropdown({
|
|
2313
2742
|
label,
|
|
2314
2743
|
open,
|
|
@@ -2317,14 +2746,14 @@ function BubbleDropdown({
|
|
|
2317
2746
|
triggerClassName = "",
|
|
2318
2747
|
panelClassName = ""
|
|
2319
2748
|
}) {
|
|
2320
|
-
const triggerRef =
|
|
2321
|
-
const panelRef =
|
|
2322
|
-
const [position, setPosition] =
|
|
2323
|
-
const [isMounted, setIsMounted] =
|
|
2324
|
-
|
|
2749
|
+
const triggerRef = useRef7(null);
|
|
2750
|
+
const panelRef = useRef7(null);
|
|
2751
|
+
const [position, setPosition] = useState6(null);
|
|
2752
|
+
const [isMounted, setIsMounted] = useState6(false);
|
|
2753
|
+
useEffect6(() => {
|
|
2325
2754
|
setIsMounted(true);
|
|
2326
2755
|
}, []);
|
|
2327
|
-
const calculatePosition =
|
|
2756
|
+
const calculatePosition = useCallback7(() => {
|
|
2328
2757
|
if (!triggerRef.current) return null;
|
|
2329
2758
|
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
2330
2759
|
const gap = 4;
|
|
@@ -2340,7 +2769,7 @@ function BubbleDropdown({
|
|
|
2340
2769
|
}
|
|
2341
2770
|
return { top, left, adjusted: false };
|
|
2342
2771
|
}, []);
|
|
2343
|
-
|
|
2772
|
+
useEffect6(() => {
|
|
2344
2773
|
if (open) {
|
|
2345
2774
|
const pos = calculatePosition();
|
|
2346
2775
|
setPosition(pos);
|
|
@@ -2369,7 +2798,7 @@ function BubbleDropdown({
|
|
|
2369
2798
|
setPosition((prev) => prev ? { ...prev, adjusted: true } : null);
|
|
2370
2799
|
}
|
|
2371
2800
|
}, [open, position]);
|
|
2372
|
-
|
|
2801
|
+
useEffect6(() => {
|
|
2373
2802
|
if (!open) return;
|
|
2374
2803
|
const handleClickOutside = (e) => {
|
|
2375
2804
|
if (triggerRef.current?.contains(e.target) || panelRef.current?.contains(e.target)) {
|
|
@@ -2422,13 +2851,13 @@ function BubbleDropdown({
|
|
|
2422
2851
|
onClick: handleTriggerClick,
|
|
2423
2852
|
onMouseDown: handleTriggerMouseDown,
|
|
2424
2853
|
children: [
|
|
2425
|
-
/* @__PURE__ */
|
|
2426
|
-
/* @__PURE__ */
|
|
2854
|
+
/* @__PURE__ */ jsx8("span", { className: "ya-bubble-dropdown-label", children: label }),
|
|
2855
|
+
/* @__PURE__ */ jsx8(ChevronDownIcon, { size: 10, className: `ya-bubble-dropdown-arrow ${open ? "is-open" : ""}` })
|
|
2427
2856
|
]
|
|
2428
2857
|
}
|
|
2429
2858
|
),
|
|
2430
2859
|
isMounted && open && position && createPortal3(
|
|
2431
|
-
/* @__PURE__ */
|
|
2860
|
+
/* @__PURE__ */ jsx8(
|
|
2432
2861
|
"div",
|
|
2433
2862
|
{
|
|
2434
2863
|
ref: panelRef,
|
|
@@ -2444,8 +2873,8 @@ function BubbleDropdown({
|
|
|
2444
2873
|
}
|
|
2445
2874
|
|
|
2446
2875
|
// src/components/FontSizePicker.tsx
|
|
2447
|
-
import { useState as
|
|
2448
|
-
import { jsx as
|
|
2876
|
+
import { useState as useState7, useEffect as useEffect7, useCallback as useCallback8, useRef as useRef8 } from "react";
|
|
2877
|
+
import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2449
2878
|
var SIZE_PRESETS = [
|
|
2450
2879
|
{ name: "S", value: "0.875rem" },
|
|
2451
2880
|
{ name: "M", value: "1rem" },
|
|
@@ -2502,28 +2931,28 @@ function normalizeValue(input) {
|
|
|
2502
2931
|
return `${num}${unit}`;
|
|
2503
2932
|
}
|
|
2504
2933
|
function FontSizePicker({ value, onChange, onClose }) {
|
|
2505
|
-
const [inputValue, setInputValue] =
|
|
2506
|
-
const inputRef =
|
|
2507
|
-
|
|
2934
|
+
const [inputValue, setInputValue] = useState7(value || "");
|
|
2935
|
+
const inputRef = useRef8(null);
|
|
2936
|
+
useEffect7(() => {
|
|
2508
2937
|
inputRef.current?.focus();
|
|
2509
2938
|
inputRef.current?.select();
|
|
2510
2939
|
}, []);
|
|
2511
|
-
|
|
2940
|
+
useEffect7(() => {
|
|
2512
2941
|
setInputValue(value || "");
|
|
2513
2942
|
}, [value]);
|
|
2514
|
-
const handlePresetClick =
|
|
2943
|
+
const handlePresetClick = useCallback8((e, presetValue) => {
|
|
2515
2944
|
e.stopPropagation();
|
|
2516
2945
|
onChange(presetValue);
|
|
2517
2946
|
onClose();
|
|
2518
2947
|
}, [onChange, onClose]);
|
|
2519
|
-
const handlePresetMouseDown =
|
|
2948
|
+
const handlePresetMouseDown = useCallback8((e) => {
|
|
2520
2949
|
e.preventDefault();
|
|
2521
2950
|
e.stopPropagation();
|
|
2522
2951
|
}, []);
|
|
2523
|
-
const handleInputChange =
|
|
2952
|
+
const handleInputChange = useCallback8((e) => {
|
|
2524
2953
|
setInputValue(e.target.value);
|
|
2525
2954
|
}, []);
|
|
2526
|
-
const handleInputKeyDown =
|
|
2955
|
+
const handleInputKeyDown = useCallback8((e) => {
|
|
2527
2956
|
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
|
2528
2957
|
e.preventDefault();
|
|
2529
2958
|
e.stopPropagation();
|
|
@@ -2565,7 +2994,7 @@ function FontSizePicker({ value, onChange, onClose }) {
|
|
|
2565
2994
|
}, [inputValue, value, onChange, onClose]);
|
|
2566
2995
|
const activePreset = getPresetName(value);
|
|
2567
2996
|
return /* @__PURE__ */ jsxs4("div", { className: "ya-font-size-picker", children: [
|
|
2568
|
-
/* @__PURE__ */
|
|
2997
|
+
/* @__PURE__ */ jsx9("div", { className: "ya-size-presets", children: SIZE_PRESETS.map((preset) => /* @__PURE__ */ jsxs4(
|
|
2569
2998
|
"button",
|
|
2570
2999
|
{
|
|
2571
3000
|
type: "button",
|
|
@@ -2573,13 +3002,13 @@ function FontSizePicker({ value, onChange, onClose }) {
|
|
|
2573
3002
|
onClick: (e) => handlePresetClick(e, preset.value),
|
|
2574
3003
|
onMouseDown: handlePresetMouseDown,
|
|
2575
3004
|
children: [
|
|
2576
|
-
/* @__PURE__ */
|
|
2577
|
-
/* @__PURE__ */
|
|
3005
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-size-preset-name", children: preset.name }),
|
|
3006
|
+
/* @__PURE__ */ jsx9("span", { className: "ya-size-preset-value", children: preset.value })
|
|
2578
3007
|
]
|
|
2579
3008
|
},
|
|
2580
3009
|
preset.name
|
|
2581
3010
|
)) }),
|
|
2582
|
-
/* @__PURE__ */
|
|
3011
|
+
/* @__PURE__ */ jsx9("div", { className: "ya-size-combobox", children: /* @__PURE__ */ jsx9(
|
|
2583
3012
|
"input",
|
|
2584
3013
|
{
|
|
2585
3014
|
ref: inputRef,
|
|
@@ -2599,8 +3028,8 @@ function FontSizePicker({ value, onChange, onClose }) {
|
|
|
2599
3028
|
}
|
|
2600
3029
|
|
|
2601
3030
|
// src/components/FontWeightPicker.tsx
|
|
2602
|
-
import { useCallback as
|
|
2603
|
-
import { jsx as
|
|
3031
|
+
import { useCallback as useCallback9 } from "react";
|
|
3032
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
2604
3033
|
var WEIGHT_PRESETS = [
|
|
2605
3034
|
{ name: "Regular", value: "400" },
|
|
2606
3035
|
{ name: "Semi-bold", value: "600" },
|
|
@@ -2614,16 +3043,16 @@ function getFontWeightLabel(value) {
|
|
|
2614
3043
|
return value;
|
|
2615
3044
|
}
|
|
2616
3045
|
function FontWeightPicker({ value, onChange, onClose }) {
|
|
2617
|
-
const handleClick =
|
|
3046
|
+
const handleClick = useCallback9((e, weightValue) => {
|
|
2618
3047
|
e.stopPropagation();
|
|
2619
3048
|
onChange(weightValue);
|
|
2620
3049
|
onClose();
|
|
2621
3050
|
}, [onChange, onClose]);
|
|
2622
|
-
const handleMouseDown =
|
|
3051
|
+
const handleMouseDown = useCallback9((e) => {
|
|
2623
3052
|
e.preventDefault();
|
|
2624
3053
|
e.stopPropagation();
|
|
2625
3054
|
}, []);
|
|
2626
|
-
return /* @__PURE__ */
|
|
3055
|
+
return /* @__PURE__ */ jsx10("div", { className: "ya-font-weight-picker", children: WEIGHT_PRESETS.map((preset) => /* @__PURE__ */ jsx10(
|
|
2627
3056
|
"button",
|
|
2628
3057
|
{
|
|
2629
3058
|
type: "button",
|
|
@@ -2986,7 +3415,7 @@ body.builder-selector-active .ya-text-editable:not(.ya-text-editing) *::-moz-sel
|
|
|
2986
3415
|
styleInject('.ya-ai-editing {\n position: relative;\n}\n.ya-ai-editing::before {\n content: "";\n position: absolute;\n inset: -4px;\n border: 2px solid;\n border-radius: 6px;\n animation: ya-ai-focus-pulse 1.5s ease-in-out infinite;\n pointer-events: none;\n z-index: 10;\n}\n@keyframes ya-ai-focus-pulse {\n 0%, 100% {\n border-color: rgba(239, 68, 68, 0.6);\n box-shadow: 0 0 15px rgba(239, 68, 68, 0.2);\n }\n 50% {\n border-color: rgba(249, 115, 22, 0.8);\n box-shadow: 0 0 25px rgba(249, 115, 22, 0.3);\n }\n}\n.ya-typing-cursor {\n display: inline-block;\n width: 2px;\n height: 1.1em;\n background:\n linear-gradient(\n 180deg,\n #EF4444,\n #F97316);\n animation: ya-cursor-blink 0.5s step-end infinite;\n margin-left: 1px;\n vertical-align: text-bottom;\n border-radius: 1px;\n}\n@keyframes ya-cursor-blink {\n 50% {\n opacity: 0;\n }\n}\n.ya-ai-complete {\n animation: ya-complete-glow 0.4s ease-out forwards;\n}\n@keyframes ya-complete-glow {\n 0% {\n box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.5);\n }\n 50% {\n box-shadow: 0 0 20px 5px rgba(16, 185, 129, 0.3);\n }\n 100% {\n box-shadow: 0 0 0 0 transparent;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .ya-ai-editing::before {\n animation: none;\n border-color: rgba(239, 68, 68, 0.6);\n box-shadow: 0 0 15px rgba(239, 68, 68, 0.2);\n }\n .ya-typing-cursor {\n animation: none;\n opacity: 1;\n }\n .ya-ai-complete {\n animation: ya-complete-glow-reduced 0.2s ease-out forwards;\n }\n @keyframes ya-complete-glow-reduced {\n 0% {\n background-color: rgba(16, 185, 129, 0.1);\n }\n 100% {\n background-color: transparent;\n }\n }\n}\n.ya-ai-hidden {\n opacity: 0;\n visibility: hidden;\n}\n.ya-ai-fade-in {\n animation: ya-fade-in 0.3s ease-out forwards;\n}\n@keyframes ya-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n.ya-ai-pulse {\n animation: ya-scale-pulse 0.3s ease-out forwards;\n}\n@keyframes ya-scale-pulse {\n 0% {\n transform: scale(1);\n }\n 50% {\n transform: scale(1.02);\n }\n 100% {\n transform: scale(1);\n }\n}\n');
|
|
2987
3416
|
|
|
2988
3417
|
// src/components/YaText.tsx
|
|
2989
|
-
import { Fragment as Fragment3, jsx as
|
|
3418
|
+
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2990
3419
|
var FontSize = Extension.create({
|
|
2991
3420
|
name: "fontSize",
|
|
2992
3421
|
addOptions() {
|
|
@@ -3066,13 +3495,15 @@ var FontWeight = Extension.create({
|
|
|
3066
3495
|
}
|
|
3067
3496
|
});
|
|
3068
3497
|
function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
3069
|
-
const
|
|
3070
|
-
const
|
|
3498
|
+
const contentHandle = useContent(fieldId);
|
|
3499
|
+
const { mode } = contentHandle;
|
|
3500
|
+
const { activeFieldId, setActiveField } = useContentStore();
|
|
3501
|
+
const storeContent = contentHandle.get();
|
|
3071
3502
|
const content = storeContent || (typeof children === "string" ? children : "");
|
|
3072
|
-
const [isEditing, setIsEditing] =
|
|
3073
|
-
const [showBubbleMenu, setShowBubbleMenu] =
|
|
3074
|
-
const [fontSizeOpen, setFontSizeOpen] =
|
|
3075
|
-
const [fontWeightOpen, setFontWeightOpen] =
|
|
3503
|
+
const [isEditing, setIsEditing] = useState8(false);
|
|
3504
|
+
const [showBubbleMenu, setShowBubbleMenu] = useState8(false);
|
|
3505
|
+
const [fontSizeOpen, setFontSizeOpen] = useState8(false);
|
|
3506
|
+
const [fontWeightOpen, setFontWeightOpen] = useState8(false);
|
|
3076
3507
|
const {
|
|
3077
3508
|
displayContent,
|
|
3078
3509
|
isAnimating,
|
|
@@ -3080,16 +3511,16 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3080
3511
|
} = useAnimatedText(fieldId, content, {
|
|
3081
3512
|
enabled: mode === "inline-edit" && !isEditing
|
|
3082
3513
|
});
|
|
3083
|
-
const [originalContent, setOriginalContent] =
|
|
3084
|
-
const containerRef =
|
|
3085
|
-
const originalContentRef =
|
|
3086
|
-
const originalHadLineBreaksRef =
|
|
3087
|
-
const [actionButtonsPos, setActionButtonsPos] =
|
|
3088
|
-
const handleSaveRef =
|
|
3514
|
+
const [originalContent, setOriginalContent] = useState8(content);
|
|
3515
|
+
const containerRef = useRef9(null);
|
|
3516
|
+
const originalContentRef = useRef9(content);
|
|
3517
|
+
const originalHadLineBreaksRef = useRef9(false);
|
|
3518
|
+
const [actionButtonsPos, setActionButtonsPos] = useState8(null);
|
|
3519
|
+
const handleSaveRef = useRef9(() => {
|
|
3089
3520
|
});
|
|
3090
|
-
const handleCancelRef =
|
|
3521
|
+
const handleCancelRef = useRef9(() => {
|
|
3091
3522
|
});
|
|
3092
|
-
const handleCloseRef =
|
|
3523
|
+
const handleCloseRef = useRef9(() => {
|
|
3093
3524
|
});
|
|
3094
3525
|
const editor = useEditor({
|
|
3095
3526
|
extensions: [
|
|
@@ -3145,19 +3576,19 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3145
3576
|
},
|
|
3146
3577
|
parseOptions: { preserveWhitespace: "full" }
|
|
3147
3578
|
});
|
|
3148
|
-
|
|
3579
|
+
useEffect8(() => {
|
|
3149
3580
|
if (editor && !isEditing) {
|
|
3150
3581
|
if (editor.getHTML() !== content) {
|
|
3151
3582
|
editor.commands.setContent(content, { parseOptions: { preserveWhitespace: "full" } });
|
|
3152
3583
|
}
|
|
3153
3584
|
}
|
|
3154
3585
|
}, [content, editor, isEditing]);
|
|
3155
|
-
|
|
3586
|
+
useEffect8(() => {
|
|
3156
3587
|
if (isEditing && activeFieldId !== null && activeFieldId !== fieldId) {
|
|
3157
3588
|
setIsEditing(false);
|
|
3158
3589
|
}
|
|
3159
3590
|
}, [activeFieldId, fieldId, isEditing]);
|
|
3160
|
-
|
|
3591
|
+
useEffect8(() => {
|
|
3161
3592
|
if (!isEditing || !containerRef.current || !editor) {
|
|
3162
3593
|
setActionButtonsPos(null);
|
|
3163
3594
|
return;
|
|
@@ -3182,31 +3613,31 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3182
3613
|
window.removeEventListener("resize", updatePosition);
|
|
3183
3614
|
};
|
|
3184
3615
|
}, [isEditing, editor]);
|
|
3185
|
-
const handleSave =
|
|
3616
|
+
const handleSave = useCallback10(() => {
|
|
3186
3617
|
if (!editor) return;
|
|
3187
3618
|
let html = editor.getHTML();
|
|
3188
3619
|
const separator = originalHadLineBreaksRef.current ? "<br><br>" : " ";
|
|
3189
3620
|
html = html.replace(/<\/p><p>/g, separator).replace(/^<p>/, "").replace(/<\/p>$/, "");
|
|
3190
3621
|
if (html !== originalContentRef.current) {
|
|
3191
|
-
|
|
3192
|
-
|
|
3622
|
+
contentHandle.set(html, "user");
|
|
3623
|
+
contentHandle.save();
|
|
3193
3624
|
originalContentRef.current = html;
|
|
3194
3625
|
}
|
|
3195
3626
|
setShowBubbleMenu(false);
|
|
3196
3627
|
setIsEditing(false);
|
|
3197
|
-
}, [editor,
|
|
3198
|
-
const handleCancel =
|
|
3628
|
+
}, [editor, contentHandle]);
|
|
3629
|
+
const handleCancel = useCallback10(() => {
|
|
3199
3630
|
if (editor) {
|
|
3200
3631
|
editor.commands.setContent(originalContent, { parseOptions: { preserveWhitespace: "full" } });
|
|
3201
3632
|
}
|
|
3202
3633
|
setShowBubbleMenu(false);
|
|
3203
3634
|
setIsEditing(false);
|
|
3204
3635
|
}, [editor, originalContent]);
|
|
3205
|
-
|
|
3636
|
+
useEffect8(() => {
|
|
3206
3637
|
handleSaveRef.current = handleSave;
|
|
3207
3638
|
handleCancelRef.current = handleCancel;
|
|
3208
3639
|
}, [handleSave, handleCancel]);
|
|
3209
|
-
|
|
3640
|
+
useEffect8(() => {
|
|
3210
3641
|
if (mode !== "inline-edit") return;
|
|
3211
3642
|
const handleEditRequest = (event) => {
|
|
3212
3643
|
const customEvent = event;
|
|
@@ -3229,7 +3660,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3229
3660
|
window.addEventListener("yatext:edit-mode", handleEditRequest);
|
|
3230
3661
|
return () => window.removeEventListener("yatext:edit-mode", handleEditRequest);
|
|
3231
3662
|
}, [mode, fieldId, content, editor, setActiveField]);
|
|
3232
|
-
const handleClose =
|
|
3663
|
+
const handleClose = useCallback10(() => {
|
|
3233
3664
|
if (!editor) {
|
|
3234
3665
|
setShowBubbleMenu(false);
|
|
3235
3666
|
setIsEditing(false);
|
|
@@ -3239,16 +3670,16 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3239
3670
|
const separator = originalHadLineBreaksRef.current ? "<br><br>" : " ";
|
|
3240
3671
|
currentHtml = currentHtml.replace(/<\/p><p>/g, separator).replace(/^<p>/, "").replace(/<\/p>$/, "");
|
|
3241
3672
|
if (currentHtml !== originalContentRef.current) {
|
|
3242
|
-
|
|
3243
|
-
|
|
3673
|
+
contentHandle.set(currentHtml, "user");
|
|
3674
|
+
contentHandle.save();
|
|
3244
3675
|
}
|
|
3245
3676
|
setShowBubbleMenu(false);
|
|
3246
3677
|
setIsEditing(false);
|
|
3247
|
-
}, [editor,
|
|
3248
|
-
|
|
3678
|
+
}, [editor, contentHandle]);
|
|
3679
|
+
useEffect8(() => {
|
|
3249
3680
|
handleCloseRef.current = handleClose;
|
|
3250
3681
|
}, [handleClose]);
|
|
3251
|
-
const handleClick =
|
|
3682
|
+
const handleClick = useCallback10((e) => {
|
|
3252
3683
|
if (isEditing) {
|
|
3253
3684
|
e.preventDefault();
|
|
3254
3685
|
e.stopPropagation();
|
|
@@ -3279,7 +3710,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3279
3710
|
}, 20);
|
|
3280
3711
|
}
|
|
3281
3712
|
}, [mode, isEditing, content, editor, fieldId, setActiveField, handleClose]);
|
|
3282
|
-
const handleKeyDown =
|
|
3713
|
+
const handleKeyDown = useCallback10(
|
|
3283
3714
|
(event) => {
|
|
3284
3715
|
if (!isEditing) return;
|
|
3285
3716
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
@@ -3300,7 +3731,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3300
3731
|
},
|
|
3301
3732
|
[isEditing, handleSave, handleCancel]
|
|
3302
3733
|
);
|
|
3303
|
-
const handleLink =
|
|
3734
|
+
const handleLink = useCallback10(() => {
|
|
3304
3735
|
if (!editor) return;
|
|
3305
3736
|
const previousUrl = editor.getAttributes("link").href;
|
|
3306
3737
|
const url = window.prompt("Enter URL:", previousUrl || "https://");
|
|
@@ -3311,7 +3742,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3311
3742
|
editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
|
|
3312
3743
|
}
|
|
3313
3744
|
}, [editor]);
|
|
3314
|
-
const handleFontSizeChange =
|
|
3745
|
+
const handleFontSizeChange = useCallback10(
|
|
3315
3746
|
(size) => {
|
|
3316
3747
|
if (!editor) return;
|
|
3317
3748
|
if (size === "") {
|
|
@@ -3322,7 +3753,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3322
3753
|
},
|
|
3323
3754
|
[editor]
|
|
3324
3755
|
);
|
|
3325
|
-
const handleFontWeightChange =
|
|
3756
|
+
const handleFontWeightChange = useCallback10(
|
|
3326
3757
|
(weight) => {
|
|
3327
3758
|
if (!editor) return;
|
|
3328
3759
|
if (weight === "") {
|
|
@@ -3367,14 +3798,14 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3367
3798
|
return "";
|
|
3368
3799
|
};
|
|
3369
3800
|
if (mode === "read-only") {
|
|
3370
|
-
return /* @__PURE__ */
|
|
3801
|
+
return /* @__PURE__ */ jsx11(
|
|
3371
3802
|
Component,
|
|
3372
3803
|
{
|
|
3373
3804
|
ref: containerRef,
|
|
3374
3805
|
className,
|
|
3375
3806
|
"data-ya-restricted": "true",
|
|
3376
3807
|
"data-field-id": fieldId,
|
|
3377
|
-
children: /* @__PURE__ */
|
|
3808
|
+
children: /* @__PURE__ */ jsx11(SafeHtml, { content, mode })
|
|
3378
3809
|
}
|
|
3379
3810
|
);
|
|
3380
3811
|
}
|
|
@@ -3387,7 +3818,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3387
3818
|
wrapperClassName
|
|
3388
3819
|
].filter(Boolean).join(" ");
|
|
3389
3820
|
const EditSafeComponent = isEditing && Component === "p" ? "div" : Component;
|
|
3390
|
-
return /* @__PURE__ */
|
|
3821
|
+
return /* @__PURE__ */ jsx11(
|
|
3391
3822
|
EditSafeComponent,
|
|
3392
3823
|
{
|
|
3393
3824
|
ref: containerRef,
|
|
@@ -3405,7 +3836,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3405
3836
|
open: showBubbleMenu,
|
|
3406
3837
|
className: "ya-bubble-menu",
|
|
3407
3838
|
children: [
|
|
3408
|
-
/* @__PURE__ */
|
|
3839
|
+
/* @__PURE__ */ jsx11(
|
|
3409
3840
|
"button",
|
|
3410
3841
|
{
|
|
3411
3842
|
type: "button",
|
|
@@ -3413,10 +3844,10 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3413
3844
|
onMouseDown: (e) => e.preventDefault(),
|
|
3414
3845
|
className: `ya-bubble-btn ${editor.isActive("bold") ? "is-active" : ""}`,
|
|
3415
3846
|
title: "Bold",
|
|
3416
|
-
children: /* @__PURE__ */
|
|
3847
|
+
children: /* @__PURE__ */ jsx11(BoldIcon, { size: 16 })
|
|
3417
3848
|
}
|
|
3418
3849
|
),
|
|
3419
|
-
/* @__PURE__ */
|
|
3850
|
+
/* @__PURE__ */ jsx11(
|
|
3420
3851
|
"button",
|
|
3421
3852
|
{
|
|
3422
3853
|
type: "button",
|
|
@@ -3424,10 +3855,10 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3424
3855
|
onMouseDown: (e) => e.preventDefault(),
|
|
3425
3856
|
className: `ya-bubble-btn ${editor.isActive("italic") ? "is-active" : ""}`,
|
|
3426
3857
|
title: "Italic",
|
|
3427
|
-
children: /* @__PURE__ */
|
|
3858
|
+
children: /* @__PURE__ */ jsx11(ItalicIcon, { size: 16 })
|
|
3428
3859
|
}
|
|
3429
3860
|
),
|
|
3430
|
-
/* @__PURE__ */
|
|
3861
|
+
/* @__PURE__ */ jsx11(
|
|
3431
3862
|
"button",
|
|
3432
3863
|
{
|
|
3433
3864
|
type: "button",
|
|
@@ -3435,17 +3866,17 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3435
3866
|
onMouseDown: (e) => e.preventDefault(),
|
|
3436
3867
|
className: `ya-bubble-btn ${editor.isActive("link") ? "is-active" : ""}`,
|
|
3437
3868
|
title: "Link",
|
|
3438
|
-
children: /* @__PURE__ */
|
|
3869
|
+
children: /* @__PURE__ */ jsx11(LinkIcon2, { size: 16 })
|
|
3439
3870
|
}
|
|
3440
3871
|
),
|
|
3441
|
-
/* @__PURE__ */
|
|
3442
|
-
/* @__PURE__ */
|
|
3872
|
+
/* @__PURE__ */ jsx11("span", { className: "ya-bubble-divider" }),
|
|
3873
|
+
/* @__PURE__ */ jsx11(
|
|
3443
3874
|
BubbleDropdown,
|
|
3444
3875
|
{
|
|
3445
3876
|
label: getFontSizeLabel(getCurrentFontSize()),
|
|
3446
3877
|
open: fontSizeOpen,
|
|
3447
3878
|
onOpenChange: setFontSizeOpen,
|
|
3448
|
-
children: /* @__PURE__ */
|
|
3879
|
+
children: /* @__PURE__ */ jsx11(
|
|
3449
3880
|
FontSizePicker,
|
|
3450
3881
|
{
|
|
3451
3882
|
value: getCurrentFontSize(),
|
|
@@ -3455,13 +3886,13 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3455
3886
|
)
|
|
3456
3887
|
}
|
|
3457
3888
|
),
|
|
3458
|
-
/* @__PURE__ */
|
|
3889
|
+
/* @__PURE__ */ jsx11(
|
|
3459
3890
|
BubbleDropdown,
|
|
3460
3891
|
{
|
|
3461
3892
|
label: getFontWeightLabel(getCurrentFontWeight()),
|
|
3462
3893
|
open: fontWeightOpen,
|
|
3463
3894
|
onOpenChange: setFontWeightOpen,
|
|
3464
|
-
children: /* @__PURE__ */
|
|
3895
|
+
children: /* @__PURE__ */ jsx11(
|
|
3465
3896
|
FontWeightPicker,
|
|
3466
3897
|
{
|
|
3467
3898
|
value: getCurrentFontWeight(),
|
|
@@ -3475,7 +3906,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3475
3906
|
}
|
|
3476
3907
|
),
|
|
3477
3908
|
isEditing ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3478
|
-
/* @__PURE__ */
|
|
3909
|
+
/* @__PURE__ */ jsx11(EditorContent, { editor }),
|
|
3479
3910
|
actionButtonsPos && createPortal4(
|
|
3480
3911
|
/* @__PURE__ */ jsxs5(
|
|
3481
3912
|
"div",
|
|
@@ -3489,7 +3920,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3489
3920
|
// Right edge aligns with text end
|
|
3490
3921
|
},
|
|
3491
3922
|
children: [
|
|
3492
|
-
/* @__PURE__ */
|
|
3923
|
+
/* @__PURE__ */ jsx11(
|
|
3493
3924
|
"button",
|
|
3494
3925
|
{
|
|
3495
3926
|
type: "button",
|
|
@@ -3498,7 +3929,7 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3498
3929
|
children: "Cancel"
|
|
3499
3930
|
}
|
|
3500
3931
|
),
|
|
3501
|
-
/* @__PURE__ */
|
|
3932
|
+
/* @__PURE__ */ jsx11(
|
|
3502
3933
|
"button",
|
|
3503
3934
|
{
|
|
3504
3935
|
type: "button",
|
|
@@ -3513,19 +3944,19 @@ function YaText({ fieldId, className, as: Component = "span", children }) {
|
|
|
3513
3944
|
document.body
|
|
3514
3945
|
)
|
|
3515
3946
|
] }) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3516
|
-
/* @__PURE__ */
|
|
3517
|
-
isAnimating && /* @__PURE__ */
|
|
3947
|
+
/* @__PURE__ */ jsx11(SafeHtml, { content: displayContent, mode }),
|
|
3948
|
+
isAnimating && /* @__PURE__ */ jsx11("span", { className: "ya-typing-cursor" })
|
|
3518
3949
|
] })
|
|
3519
3950
|
] }) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3520
|
-
/* @__PURE__ */
|
|
3521
|
-
isAnimating && /* @__PURE__ */
|
|
3951
|
+
/* @__PURE__ */ jsx11(SafeHtml, { content: displayContent, mode }),
|
|
3952
|
+
isAnimating && /* @__PURE__ */ jsx11("span", { className: "ya-typing-cursor" })
|
|
3522
3953
|
] })
|
|
3523
3954
|
}
|
|
3524
3955
|
);
|
|
3525
3956
|
}
|
|
3526
3957
|
|
|
3527
3958
|
// src/components/YaImage.tsx
|
|
3528
|
-
import { useCallback as
|
|
3959
|
+
import { useCallback as useCallback13, useEffect as useEffect11, useRef as useRef12, useState as useState11 } from "react";
|
|
3529
3960
|
|
|
3530
3961
|
// src/lib/asset-resolver.ts
|
|
3531
3962
|
var assetResolver = (path) => path;
|
|
@@ -3541,25 +3972,25 @@ function resolveAssetUrl(path) {
|
|
|
3541
3972
|
}
|
|
3542
3973
|
|
|
3543
3974
|
// src/components/YaTooltip.tsx
|
|
3544
|
-
import { useEffect as
|
|
3975
|
+
import { useEffect as useEffect9, useRef as useRef10, useState as useState9, useCallback as useCallback11 } from "react";
|
|
3545
3976
|
import { createPortal as createPortal5 } from "react-dom";
|
|
3546
3977
|
|
|
3547
3978
|
// src/components/ya-tooltip.css
|
|
3548
3979
|
styleInject('.ya-tooltip {\n position: fixed;\n z-index: 9999;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n background: #1a1a1a;\n color: white;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n white-space: nowrap;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n animation: ya-tooltip-fade-in 0.15s ease;\n pointer-events: none;\n}\n@keyframes ya-tooltip-fade-in {\n from {\n transform: scale(0.95);\n }\n to {\n transform: scale(1);\n }\n}\n.ya-tooltip svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n}\n.ya-tooltip-bottom {\n transform: translateX(-50%);\n}\n.ya-tooltip-bottom::before {\n content: "";\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 6px solid transparent;\n border-bottom-color: #1a1a1a;\n}\n.ya-tooltip-top {\n transform: translateX(-50%);\n}\n.ya-tooltip-top::before {\n content: "";\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 6px solid transparent;\n border-top-color: #1a1a1a;\n}\n.ya-tooltip-right {\n transform: translateY(-50%);\n}\n.ya-tooltip-right::before {\n content: "";\n position: absolute;\n right: 100%;\n top: 50%;\n transform: translateY(-50%);\n border: 6px solid transparent;\n border-right-color: #1a1a1a;\n}\n.ya-tooltip-left {\n transform: translateY(-50%);\n}\n.ya-tooltip-left::before {\n content: "";\n position: absolute;\n left: 100%;\n top: 50%;\n transform: translateY(-50%);\n border: 6px solid transparent;\n border-left-color: #1a1a1a;\n}\n');
|
|
3549
3980
|
|
|
3550
3981
|
// src/components/YaTooltip.tsx
|
|
3551
|
-
import { jsx as
|
|
3982
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
3552
3983
|
function YaTooltip({
|
|
3553
3984
|
anchorRef,
|
|
3554
3985
|
children,
|
|
3555
3986
|
show,
|
|
3556
3987
|
preferredPosition = "bottom"
|
|
3557
3988
|
}) {
|
|
3558
|
-
const [position, setPosition] =
|
|
3559
|
-
const [coords, setCoords] =
|
|
3560
|
-
const [isPositioned, setIsPositioned] =
|
|
3561
|
-
const tooltipRef =
|
|
3562
|
-
const calculatePosition =
|
|
3989
|
+
const [position, setPosition] = useState9(preferredPosition);
|
|
3990
|
+
const [coords, setCoords] = useState9({ top: 0, left: 0 });
|
|
3991
|
+
const [isPositioned, setIsPositioned] = useState9(false);
|
|
3992
|
+
const tooltipRef = useRef10(null);
|
|
3993
|
+
const calculatePosition = useCallback11(() => {
|
|
3563
3994
|
if (!anchorRef.current) return;
|
|
3564
3995
|
const anchor = anchorRef.current.getBoundingClientRect();
|
|
3565
3996
|
const tooltip = tooltipRef.current?.getBoundingClientRect();
|
|
@@ -3634,7 +4065,7 @@ function YaTooltip({
|
|
|
3634
4065
|
setCoords({ top, left });
|
|
3635
4066
|
setIsPositioned(true);
|
|
3636
4067
|
}, [anchorRef, preferredPosition]);
|
|
3637
|
-
|
|
4068
|
+
useEffect9(() => {
|
|
3638
4069
|
if (!show) {
|
|
3639
4070
|
setIsPositioned(false);
|
|
3640
4071
|
return;
|
|
@@ -3647,14 +4078,14 @@ function YaTooltip({
|
|
|
3647
4078
|
window.removeEventListener("resize", calculatePosition);
|
|
3648
4079
|
};
|
|
3649
4080
|
}, [show, calculatePosition]);
|
|
3650
|
-
|
|
4081
|
+
useEffect9(() => {
|
|
3651
4082
|
if (show && tooltipRef.current) {
|
|
3652
4083
|
calculatePosition();
|
|
3653
4084
|
}
|
|
3654
4085
|
}, [show, children, calculatePosition]);
|
|
3655
4086
|
if (!show) return null;
|
|
3656
4087
|
return createPortal5(
|
|
3657
|
-
/* @__PURE__ */
|
|
4088
|
+
/* @__PURE__ */ jsx12(
|
|
3658
4089
|
"div",
|
|
3659
4090
|
{
|
|
3660
4091
|
ref: tooltipRef,
|
|
@@ -3673,7 +4104,7 @@ function YaTooltip({
|
|
|
3673
4104
|
}
|
|
3674
4105
|
|
|
3675
4106
|
// src/hooks/useImageShaderTransition.ts
|
|
3676
|
-
import { useCallback as
|
|
4107
|
+
import { useCallback as useCallback12, useEffect as useEffect10, useRef as useRef11, useState as useState10 } from "react";
|
|
3677
4108
|
|
|
3678
4109
|
// src/lib/image-shader-transition.ts
|
|
3679
4110
|
var VERTEX_SHADER = `
|
|
@@ -3933,14 +4364,14 @@ function preloadImage(src) {
|
|
|
3933
4364
|
var TRANSITION_DURATION = 500;
|
|
3934
4365
|
function useImageShaderTransition(options = {}) {
|
|
3935
4366
|
const { duration = TRANSITION_DURATION, onComplete } = options;
|
|
3936
|
-
const canvasRef =
|
|
3937
|
-
const rendererRef =
|
|
3938
|
-
const rafRef =
|
|
3939
|
-
const startTimeRef =
|
|
3940
|
-
const newSrcRef =
|
|
3941
|
-
const [isTransitioning, setIsTransitioning] =
|
|
3942
|
-
const [webglAvailable] =
|
|
3943
|
-
|
|
4367
|
+
const canvasRef = useRef11(null);
|
|
4368
|
+
const rendererRef = useRef11(null);
|
|
4369
|
+
const rafRef = useRef11(null);
|
|
4370
|
+
const startTimeRef = useRef11(0);
|
|
4371
|
+
const newSrcRef = useRef11("");
|
|
4372
|
+
const [isTransitioning, setIsTransitioning] = useState10(false);
|
|
4373
|
+
const [webglAvailable] = useState10(() => isWebGLAvailable());
|
|
4374
|
+
useEffect10(() => {
|
|
3944
4375
|
return () => {
|
|
3945
4376
|
if (rafRef.current) {
|
|
3946
4377
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -3948,7 +4379,7 @@ function useImageShaderTransition(options = {}) {
|
|
|
3948
4379
|
rendererRef.current?.destroy();
|
|
3949
4380
|
};
|
|
3950
4381
|
}, []);
|
|
3951
|
-
const animate =
|
|
4382
|
+
const animate = useCallback12(() => {
|
|
3952
4383
|
const renderer = rendererRef.current;
|
|
3953
4384
|
if (!renderer) return;
|
|
3954
4385
|
const elapsed = performance.now() - startTimeRef.current;
|
|
@@ -3963,7 +4394,7 @@ function useImageShaderTransition(options = {}) {
|
|
|
3963
4394
|
});
|
|
3964
4395
|
}
|
|
3965
4396
|
}, [duration, onComplete]);
|
|
3966
|
-
const startTransition =
|
|
4397
|
+
const startTransition = useCallback12(
|
|
3967
4398
|
(oldSrc, newSrc) => {
|
|
3968
4399
|
if (!webglAvailable) {
|
|
3969
4400
|
return;
|
|
@@ -3995,7 +4426,7 @@ function useImageShaderTransition(options = {}) {
|
|
|
3995
4426
|
},
|
|
3996
4427
|
[webglAvailable, animate]
|
|
3997
4428
|
);
|
|
3998
|
-
const cancelTransition =
|
|
4429
|
+
const cancelTransition = useCallback12(() => {
|
|
3999
4430
|
if (rafRef.current) {
|
|
4000
4431
|
cancelAnimationFrame(rafRef.current);
|
|
4001
4432
|
rafRef.current = null;
|
|
@@ -4015,7 +4446,7 @@ function useImageShaderTransition(options = {}) {
|
|
|
4015
4446
|
styleInject('.ya-image-container {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 45px;\n min-height: 45px;\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-image-container img {\n display: block;\n}\n.ya-image-editable {\n cursor: pointer;\n}\n.ya-image-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-selected {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 0.2s ease;\n pointer-events: none;\n border-radius: inherit;\n}\n.ya-image-editable:hover .ya-image-overlay {\n opacity: 1;\n}\n.ya-image-selected .ya-image-overlay {\n opacity: 0;\n}\n.ya-image-edit-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: white;\n border-radius: 50%;\n color: #1a1a1a;\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);\n}\n.ya-image-edit-icon svg {\n width: 24px;\n height: 24px;\n}\n.ya-image-edit-label {\n color: white;\n font-size: 14px;\n font-weight: 500;\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);\n}\n@keyframes ya-image-success {\n 0% {\n outline-color: var(--color-primary, #D4A574);\n }\n 50% {\n outline-color: #22c55e;\n outline-width: 4px;\n }\n 100% {\n outline-color: var(--color-primary, #D4A574);\n outline-width: 2px;\n }\n}\n.ya-image-success {\n animation: ya-image-success 0.4s ease;\n}\n.ya-image-loading::after {\n content: "";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0) 100%);\n background-size: 200% 100%;\n animation: ya-image-shimmer 1.5s infinite;\n}\n@keyframes ya-image-shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n.ya-image-container:focus {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-container:focus:not(:focus-visible) {\n outline: none;\n}\n.ya-image-container:focus-visible {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-image-small .ya-image-overlay {\n display: none;\n}\n.ya-image-drop-target {\n outline: 2px dashed var(--ya-drop-color, #3b82f6) !important;\n outline-offset: 4px;\n}\n.ya-image-drop-target .ya-image-overlay {\n display: none !important;\n}\n.ya-image-drop-hover {\n outline: 3px solid var(--ya-drop-color, #3b82f6) !important;\n outline-offset: 4px;\n background-color: rgba(59, 130, 246, 0.1);\n}\n.ya-image-drop-hover::before {\n content: "";\n position: absolute;\n inset: -4px;\n border: 2px solid var(--ya-drop-color, #3b82f6);\n border-radius: inherit;\n animation: ya-drop-pulse 1s ease-in-out infinite;\n pointer-events: none;\n}\n@keyframes ya-drop-pulse {\n 0%, 100% {\n opacity: 0.4;\n transform: scale(1);\n }\n 50% {\n opacity: 0.8;\n transform: scale(1.02);\n }\n}\n');
|
|
4016
4447
|
|
|
4017
4448
|
// src/components/YaImage.tsx
|
|
4018
|
-
import { jsx as
|
|
4449
|
+
import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4019
4450
|
function parseImageValue(value) {
|
|
4020
4451
|
if (!value) {
|
|
4021
4452
|
return { src: "" };
|
|
@@ -4059,17 +4490,18 @@ function YaImage({
|
|
|
4059
4490
|
fallbackSrc,
|
|
4060
4491
|
fallbackAlt
|
|
4061
4492
|
}) {
|
|
4062
|
-
const
|
|
4063
|
-
const
|
|
4064
|
-
const
|
|
4065
|
-
const
|
|
4066
|
-
const [
|
|
4067
|
-
const [
|
|
4068
|
-
const [
|
|
4069
|
-
const [
|
|
4070
|
-
const [
|
|
4071
|
-
const
|
|
4072
|
-
const
|
|
4493
|
+
const content = useContent(fieldId);
|
|
4494
|
+
const { mode } = content;
|
|
4495
|
+
const containerRef = useRef12(null);
|
|
4496
|
+
const imgRef = useRef12(null);
|
|
4497
|
+
const [isSelected, setIsSelected] = useState11(false);
|
|
4498
|
+
const [isHovered, setIsHovered] = useState11(false);
|
|
4499
|
+
const [isSmallImage, setIsSmallImage] = useState11(false);
|
|
4500
|
+
const [isDropMode, setIsDropMode] = useState11(false);
|
|
4501
|
+
const [isDropHover, setIsDropHover] = useState11(false);
|
|
4502
|
+
const [previewOverride, setPreviewOverride] = useState11(null);
|
|
4503
|
+
const prevSrcRef = useRef12(null);
|
|
4504
|
+
const rawValue = content.get();
|
|
4073
4505
|
const imageData = parseImageValue(rawValue);
|
|
4074
4506
|
const displayData = previewOverride || imageData;
|
|
4075
4507
|
const src = displayData.src || fallbackSrc || PLACEHOLDER_SVG;
|
|
@@ -4085,36 +4517,34 @@ function YaImage({
|
|
|
4085
4517
|
} = useImageShaderTransition({
|
|
4086
4518
|
duration: 500,
|
|
4087
4519
|
onComplete: () => {
|
|
4088
|
-
clearChangeSource(
|
|
4520
|
+
content.clearChangeSource();
|
|
4089
4521
|
}
|
|
4090
4522
|
});
|
|
4091
|
-
|
|
4092
|
-
const changeSource =
|
|
4523
|
+
useEffect11(() => {
|
|
4524
|
+
const changeSource = content.changeSource;
|
|
4093
4525
|
const resolvedSrc = resolveAssetUrl(src);
|
|
4094
4526
|
const prevResolvedSrc = prevSrcRef.current;
|
|
4095
4527
|
if (changeSource === "ai") {
|
|
4096
4528
|
if (prevResolvedSrc !== null && prevResolvedSrc !== resolvedSrc && webglAvailable) {
|
|
4097
4529
|
startTransition(prevResolvedSrc, resolvedSrc);
|
|
4098
4530
|
} else {
|
|
4099
|
-
clearChangeSource(
|
|
4531
|
+
content.clearChangeSource();
|
|
4100
4532
|
}
|
|
4101
4533
|
}
|
|
4102
4534
|
prevSrcRef.current = resolvedSrc;
|
|
4103
4535
|
}, [
|
|
4104
4536
|
rawValue,
|
|
4105
4537
|
src,
|
|
4106
|
-
|
|
4107
|
-
getChangeSource,
|
|
4108
|
-
clearChangeSource,
|
|
4538
|
+
content,
|
|
4109
4539
|
startTransition,
|
|
4110
4540
|
webglAvailable
|
|
4111
4541
|
]);
|
|
4112
|
-
|
|
4542
|
+
useEffect11(() => {
|
|
4113
4543
|
return () => {
|
|
4114
4544
|
cancelTransition();
|
|
4115
4545
|
};
|
|
4116
4546
|
}, [cancelTransition]);
|
|
4117
|
-
const handleClick =
|
|
4547
|
+
const handleClick = useCallback13(() => {
|
|
4118
4548
|
if (mode !== "inline-edit") return;
|
|
4119
4549
|
if (document.body.classList.contains("builder-selector-active")) return;
|
|
4120
4550
|
setIsSelected(true);
|
|
@@ -4142,12 +4572,14 @@ function YaImage({
|
|
|
4142
4572
|
"*"
|
|
4143
4573
|
);
|
|
4144
4574
|
}, [mode, fieldId, imageData, src, altText, objectFit, objectPosition]);
|
|
4145
|
-
|
|
4575
|
+
useEffect11(() => {
|
|
4146
4576
|
if (mode !== "inline-edit") return;
|
|
4147
4577
|
const handleMessage2 = (event) => {
|
|
4148
4578
|
if (event.data?.type === "YA_IMAGE_EDIT_COMPLETE" && event.data.fieldId === fieldId) {
|
|
4149
4579
|
const value = event.data.value;
|
|
4150
|
-
|
|
4580
|
+
const serializedValue = serializeImageValue(value);
|
|
4581
|
+
content.set(serializedValue, "user");
|
|
4582
|
+
content.save();
|
|
4151
4583
|
setPreviewOverride(null);
|
|
4152
4584
|
setIsSelected(false);
|
|
4153
4585
|
}
|
|
@@ -4162,8 +4594,8 @@ function YaImage({
|
|
|
4162
4594
|
};
|
|
4163
4595
|
window.addEventListener("message", handleMessage2);
|
|
4164
4596
|
return () => window.removeEventListener("message", handleMessage2);
|
|
4165
|
-
}, [mode, fieldId,
|
|
4166
|
-
|
|
4597
|
+
}, [mode, fieldId, content]);
|
|
4598
|
+
useEffect11(() => {
|
|
4167
4599
|
if (mode !== "inline-edit") return;
|
|
4168
4600
|
const handleDropModeMessage = (event) => {
|
|
4169
4601
|
if (event.data?.type === "DROP_MODE_START") {
|
|
@@ -4177,7 +4609,7 @@ function YaImage({
|
|
|
4177
4609
|
window.addEventListener("message", handleDropModeMessage);
|
|
4178
4610
|
return () => window.removeEventListener("message", handleDropModeMessage);
|
|
4179
4611
|
}, [mode]);
|
|
4180
|
-
const handleDragEnter =
|
|
4612
|
+
const handleDragEnter = useCallback13(
|
|
4181
4613
|
(e) => {
|
|
4182
4614
|
if (!isDropMode) return;
|
|
4183
4615
|
e.preventDefault();
|
|
@@ -4201,7 +4633,7 @@ function YaImage({
|
|
|
4201
4633
|
},
|
|
4202
4634
|
[isDropMode, fieldId]
|
|
4203
4635
|
);
|
|
4204
|
-
const handleDragOver =
|
|
4636
|
+
const handleDragOver = useCallback13(
|
|
4205
4637
|
(e) => {
|
|
4206
4638
|
if (!isDropMode) return;
|
|
4207
4639
|
e.preventDefault();
|
|
@@ -4209,7 +4641,7 @@ function YaImage({
|
|
|
4209
4641
|
},
|
|
4210
4642
|
[isDropMode]
|
|
4211
4643
|
);
|
|
4212
|
-
const handleDragLeave =
|
|
4644
|
+
const handleDragLeave = useCallback13(
|
|
4213
4645
|
(e) => {
|
|
4214
4646
|
if (!isDropMode) return;
|
|
4215
4647
|
e.preventDefault();
|
|
@@ -4223,7 +4655,7 @@ function YaImage({
|
|
|
4223
4655
|
},
|
|
4224
4656
|
[isDropMode]
|
|
4225
4657
|
);
|
|
4226
|
-
const handleDrop =
|
|
4658
|
+
const handleDrop = useCallback13(
|
|
4227
4659
|
(e) => {
|
|
4228
4660
|
if (!isDropMode) return;
|
|
4229
4661
|
e.preventDefault();
|
|
@@ -4241,7 +4673,7 @@ function YaImage({
|
|
|
4241
4673
|
},
|
|
4242
4674
|
[isDropMode, fieldId]
|
|
4243
4675
|
);
|
|
4244
|
-
|
|
4676
|
+
useEffect11(() => {
|
|
4245
4677
|
if (mode !== "inline-edit") return;
|
|
4246
4678
|
const checkSize = () => {
|
|
4247
4679
|
if (imgRef.current) {
|
|
@@ -4263,7 +4695,7 @@ function YaImage({
|
|
|
4263
4695
|
window.removeEventListener("resize", checkSize);
|
|
4264
4696
|
};
|
|
4265
4697
|
}, [mode]);
|
|
4266
|
-
|
|
4698
|
+
useEffect11(() => {
|
|
4267
4699
|
if (!isSelected || mode !== "inline-edit") return;
|
|
4268
4700
|
let lastRectKey = "";
|
|
4269
4701
|
let lastTime = 0;
|
|
@@ -4305,7 +4737,7 @@ function YaImage({
|
|
|
4305
4737
|
"data-ya-restricted": "true",
|
|
4306
4738
|
"data-field-id": fieldId,
|
|
4307
4739
|
children: [
|
|
4308
|
-
/* @__PURE__ */
|
|
4740
|
+
/* @__PURE__ */ jsx13(
|
|
4309
4741
|
"img",
|
|
4310
4742
|
{
|
|
4311
4743
|
src: resolveAssetUrl(src),
|
|
@@ -4321,7 +4753,7 @@ function YaImage({
|
|
|
4321
4753
|
loading
|
|
4322
4754
|
}
|
|
4323
4755
|
),
|
|
4324
|
-
/* @__PURE__ */
|
|
4756
|
+
/* @__PURE__ */ jsx13(
|
|
4325
4757
|
"canvas",
|
|
4326
4758
|
{
|
|
4327
4759
|
ref: canvasRef,
|
|
@@ -4355,9 +4787,9 @@ function YaImage({
|
|
|
4355
4787
|
strokeLinecap: "round",
|
|
4356
4788
|
strokeLinejoin: "round",
|
|
4357
4789
|
children: [
|
|
4358
|
-
/* @__PURE__ */
|
|
4359
|
-
/* @__PURE__ */
|
|
4360
|
-
/* @__PURE__ */
|
|
4790
|
+
/* @__PURE__ */ jsx13("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
4791
|
+
/* @__PURE__ */ jsx13("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
4792
|
+
/* @__PURE__ */ jsx13("polyline", { points: "21 15 16 10 5 21" })
|
|
4361
4793
|
]
|
|
4362
4794
|
}
|
|
4363
4795
|
);
|
|
@@ -4393,7 +4825,7 @@ function YaImage({
|
|
|
4393
4825
|
}
|
|
4394
4826
|
},
|
|
4395
4827
|
children: [
|
|
4396
|
-
/* @__PURE__ */
|
|
4828
|
+
/* @__PURE__ */ jsx13(
|
|
4397
4829
|
"img",
|
|
4398
4830
|
{
|
|
4399
4831
|
ref: imgRef,
|
|
@@ -4410,7 +4842,7 @@ function YaImage({
|
|
|
4410
4842
|
loading
|
|
4411
4843
|
}
|
|
4412
4844
|
),
|
|
4413
|
-
/* @__PURE__ */
|
|
4845
|
+
/* @__PURE__ */ jsx13(
|
|
4414
4846
|
"canvas",
|
|
4415
4847
|
{
|
|
4416
4848
|
ref: canvasRef,
|
|
@@ -4430,12 +4862,12 @@ function YaImage({
|
|
|
4430
4862
|
),
|
|
4431
4863
|
isSmallImage ? /* @__PURE__ */ jsxs6(YaTooltip, { anchorRef: containerRef, show: isHovered && !isSelected, children: [
|
|
4432
4864
|
editIcon,
|
|
4433
|
-
/* @__PURE__ */
|
|
4865
|
+
/* @__PURE__ */ jsx13("span", { children: "Click to edit" })
|
|
4434
4866
|
] }) : (
|
|
4435
4867
|
/* For large images: show overlay inside the image */
|
|
4436
4868
|
/* @__PURE__ */ jsxs6("div", { className: "ya-image-overlay", children: [
|
|
4437
|
-
/* @__PURE__ */
|
|
4438
|
-
/* @__PURE__ */
|
|
4869
|
+
/* @__PURE__ */ jsx13("div", { className: "ya-image-edit-icon", children: editIcon }),
|
|
4870
|
+
/* @__PURE__ */ jsx13("span", { className: "ya-image-edit-label", children: "Click to edit" })
|
|
4439
4871
|
] })
|
|
4440
4872
|
)
|
|
4441
4873
|
]
|
|
@@ -4444,13 +4876,13 @@ function YaImage({
|
|
|
4444
4876
|
}
|
|
4445
4877
|
|
|
4446
4878
|
// src/components/YaVideo.tsx
|
|
4447
|
-
import { useCallback as
|
|
4879
|
+
import { useCallback as useCallback14, useEffect as useEffect12, useRef as useRef13, useState as useState12 } from "react";
|
|
4448
4880
|
|
|
4449
4881
|
// src/components/ya-video.css
|
|
4450
4882
|
styleInject('.ya-video-wrapper {\n position: relative;\n display: block;\n width: 100%;\n}\n.ya-video-wrapper video,\n.ya-video-wrapper iframe {\n display: block;\n width: 100%;\n height: 100%;\n}\n.ya-video-container {\n position: relative;\n display: block;\n width: 100%;\n min-width: 80px;\n min-height: 45px;\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-video-container video,\n.ya-video-container iframe {\n display: block;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n.ya-video-editable {\n cursor: pointer;\n}\n.ya-video-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-video-selected {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-video-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 0.2s ease;\n pointer-events: none;\n border-radius: inherit;\n}\n.ya-video-editable:hover .ya-video-overlay {\n opacity: 1;\n}\n.ya-video-selected .ya-video-overlay {\n opacity: 0;\n}\n.ya-video-edit-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: white;\n border-radius: 50%;\n color: #1a1a1a;\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);\n}\n.ya-video-edit-icon svg {\n width: 24px;\n height: 24px;\n}\n.ya-video-edit-label {\n color: white;\n font-size: 14px;\n font-weight: 500;\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);\n}\n.ya-video-placeholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n height: 100%;\n min-height: 120px;\n background: #f3f4f6;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n color: #6b7280;\n font-size: 14px;\n}\n.ya-video-placeholder img {\n width: 64px;\n height: auto;\n opacity: 0.5;\n}\n@keyframes ya-video-success {\n 0% {\n outline-color: var(--color-primary, #D4A574);\n }\n 50% {\n outline-color: #22c55e;\n outline-width: 4px;\n }\n 100% {\n outline-color: var(--color-primary, #D4A574);\n outline-width: 2px;\n }\n}\n.ya-video-success {\n animation: ya-video-success 0.4s ease;\n}\n.ya-video-loading::after {\n content: "";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0) 100%);\n background-size: 200% 100%;\n animation: ya-video-shimmer 1.5s infinite;\n}\n@keyframes ya-video-shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n.ya-video-container:focus {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-video-container:focus:not(:focus-visible) {\n outline: none;\n}\n.ya-video-container:focus-visible {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n}\n.ya-video-small .ya-video-overlay {\n display: none;\n}\n.ya-video-background {\n position: absolute;\n inset: 0;\n z-index: -1;\n}\n.ya-video-background video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n');
|
|
4451
4883
|
|
|
4452
4884
|
// src/components/YaVideo.tsx
|
|
4453
|
-
import { jsx as
|
|
4885
|
+
import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4454
4886
|
function parseVideoValue(value) {
|
|
4455
4887
|
if (!value) {
|
|
4456
4888
|
return { type: "upload", src: "" };
|
|
@@ -4522,14 +4954,15 @@ function YaVideo({
|
|
|
4522
4954
|
fallbackSrc,
|
|
4523
4955
|
fallbackPoster
|
|
4524
4956
|
}) {
|
|
4525
|
-
const
|
|
4526
|
-
const
|
|
4527
|
-
const
|
|
4528
|
-
const
|
|
4529
|
-
const [
|
|
4530
|
-
const [
|
|
4531
|
-
const [
|
|
4532
|
-
const
|
|
4957
|
+
const content = useContent(fieldId);
|
|
4958
|
+
const { mode } = content;
|
|
4959
|
+
const containerRef = useRef13(null);
|
|
4960
|
+
const videoRef = useRef13(null);
|
|
4961
|
+
const [isSelected, setIsSelected] = useState12(false);
|
|
4962
|
+
const [isHovered, setIsHovered] = useState12(false);
|
|
4963
|
+
const [isSmallVideo, setIsSmallVideo] = useState12(false);
|
|
4964
|
+
const [isInView, setIsInView] = useState12(loading === "eager");
|
|
4965
|
+
const rawValue = content.get();
|
|
4533
4966
|
const parsedValue = parseVideoValue(rawValue);
|
|
4534
4967
|
const videoData = parsedValue.src ? parsedValue : defaultValue || parsedValue;
|
|
4535
4968
|
const src = videoData.src || fallbackSrc || "";
|
|
@@ -4542,8 +4975,8 @@ function YaVideo({
|
|
|
4542
4975
|
const controls = videoData.controls ?? true;
|
|
4543
4976
|
const playsinline = videoData.playsinline ?? true;
|
|
4544
4977
|
const preload = videoData.preload ?? "metadata";
|
|
4545
|
-
const [prefersReducedMotion, setPrefersReducedMotion] =
|
|
4546
|
-
|
|
4978
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState12(false);
|
|
4979
|
+
useEffect12(() => {
|
|
4547
4980
|
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
4548
4981
|
setPrefersReducedMotion(mediaQuery.matches);
|
|
4549
4982
|
const handleChange = (e) => {
|
|
@@ -4553,7 +4986,7 @@ function YaVideo({
|
|
|
4553
4986
|
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
4554
4987
|
}, []);
|
|
4555
4988
|
const effectiveAutoplay = autoplay && !prefersReducedMotion;
|
|
4556
|
-
|
|
4989
|
+
useEffect12(() => {
|
|
4557
4990
|
if (loading === "eager" || isInView) return;
|
|
4558
4991
|
const observer = new IntersectionObserver(
|
|
4559
4992
|
(entries) => {
|
|
@@ -4570,7 +5003,7 @@ function YaVideo({
|
|
|
4570
5003
|
}
|
|
4571
5004
|
return () => observer.disconnect();
|
|
4572
5005
|
}, [loading, isInView]);
|
|
4573
|
-
const handleKeyDown =
|
|
5006
|
+
const handleKeyDown = useCallback14(
|
|
4574
5007
|
(e) => {
|
|
4575
5008
|
if ((e.key === " " || e.key === "Enter") && videoData.type === "upload" && controls) {
|
|
4576
5009
|
e.preventDefault();
|
|
@@ -4586,7 +5019,7 @@ function YaVideo({
|
|
|
4586
5019
|
},
|
|
4587
5020
|
[videoData.type, controls]
|
|
4588
5021
|
);
|
|
4589
|
-
const handleClick =
|
|
5022
|
+
const handleClick = useCallback14(() => {
|
|
4590
5023
|
if (mode !== "inline-edit") return;
|
|
4591
5024
|
if (document.body.classList.contains("builder-selector-active")) return;
|
|
4592
5025
|
setIsSelected(true);
|
|
@@ -4606,7 +5039,7 @@ function YaVideo({
|
|
|
4606
5039
|
"*"
|
|
4607
5040
|
);
|
|
4608
5041
|
}, [mode, fieldId, videoData]);
|
|
4609
|
-
|
|
5042
|
+
useEffect12(() => {
|
|
4610
5043
|
if (mode !== "inline-edit") return;
|
|
4611
5044
|
const handleMessage2 = (event) => {
|
|
4612
5045
|
if (event.data?.type === "YA_VIDEO_EDIT_COMPLETE" && event.data.fieldId === fieldId) {
|
|
@@ -4619,7 +5052,7 @@ function YaVideo({
|
|
|
4619
5052
|
window.addEventListener("message", handleMessage2);
|
|
4620
5053
|
return () => window.removeEventListener("message", handleMessage2);
|
|
4621
5054
|
}, [mode, fieldId]);
|
|
4622
|
-
|
|
5055
|
+
useEffect12(() => {
|
|
4623
5056
|
if (mode !== "inline-edit") return;
|
|
4624
5057
|
const checkSize = () => {
|
|
4625
5058
|
if (containerRef.current) {
|
|
@@ -4631,7 +5064,7 @@ function YaVideo({
|
|
|
4631
5064
|
window.addEventListener("resize", checkSize);
|
|
4632
5065
|
return () => window.removeEventListener("resize", checkSize);
|
|
4633
5066
|
}, [mode]);
|
|
4634
|
-
|
|
5067
|
+
useEffect12(() => {
|
|
4635
5068
|
if (!isSelected || mode !== "inline-edit") return;
|
|
4636
5069
|
let lastRectKey = "";
|
|
4637
5070
|
let lastTime = 0;
|
|
@@ -4668,23 +5101,23 @@ function YaVideo({
|
|
|
4668
5101
|
const renderVideo = (isReadOnly) => {
|
|
4669
5102
|
if (!src && !isReadOnly) {
|
|
4670
5103
|
return /* @__PURE__ */ jsxs7("div", { className: "ya-video-placeholder", children: [
|
|
4671
|
-
/* @__PURE__ */
|
|
4672
|
-
/* @__PURE__ */
|
|
5104
|
+
/* @__PURE__ */ jsx14("img", { src: PLACEHOLDER_SVG2, alt: "" }),
|
|
5105
|
+
/* @__PURE__ */ jsx14("span", { children: "No video selected" })
|
|
4673
5106
|
] });
|
|
4674
5107
|
}
|
|
4675
5108
|
if (!isInView && loading === "lazy" && !isReadOnly) {
|
|
4676
|
-
return /* @__PURE__ */
|
|
5109
|
+
return /* @__PURE__ */ jsx14(
|
|
4677
5110
|
"div",
|
|
4678
5111
|
{
|
|
4679
5112
|
className: "ya-video-placeholder",
|
|
4680
5113
|
style: { aspectRatio },
|
|
4681
|
-
children: /* @__PURE__ */
|
|
5114
|
+
children: /* @__PURE__ */ jsx14("img", { src: PLACEHOLDER_SVG2, alt: "" })
|
|
4682
5115
|
}
|
|
4683
5116
|
);
|
|
4684
5117
|
}
|
|
4685
5118
|
if (videoData.type === "youtube" && src) {
|
|
4686
5119
|
const embedUrl = buildYouTubeEmbedUrl(src, videoData);
|
|
4687
|
-
return /* @__PURE__ */
|
|
5120
|
+
return /* @__PURE__ */ jsx14(
|
|
4688
5121
|
"iframe",
|
|
4689
5122
|
{
|
|
4690
5123
|
src: embedUrl,
|
|
@@ -4704,7 +5137,7 @@ function YaVideo({
|
|
|
4704
5137
|
}
|
|
4705
5138
|
if (videoData.type === "vimeo" && src) {
|
|
4706
5139
|
const embedUrl = buildVimeoEmbedUrl(src, videoData);
|
|
4707
|
-
return /* @__PURE__ */
|
|
5140
|
+
return /* @__PURE__ */ jsx14(
|
|
4708
5141
|
"iframe",
|
|
4709
5142
|
{
|
|
4710
5143
|
src: embedUrl,
|
|
@@ -4724,7 +5157,7 @@ function YaVideo({
|
|
|
4724
5157
|
}
|
|
4725
5158
|
const resolvedSrc = resolveAssetUrl(src);
|
|
4726
5159
|
const resolvedPoster = poster ? resolveAssetUrl(poster) : void 0;
|
|
4727
|
-
return /* @__PURE__ */
|
|
5160
|
+
return /* @__PURE__ */ jsx14(
|
|
4728
5161
|
"video",
|
|
4729
5162
|
{
|
|
4730
5163
|
ref: videoRef,
|
|
@@ -4759,7 +5192,7 @@ function YaVideo({
|
|
|
4759
5192
|
);
|
|
4760
5193
|
};
|
|
4761
5194
|
if (mode === "read-only") {
|
|
4762
|
-
return /* @__PURE__ */
|
|
5195
|
+
return /* @__PURE__ */ jsx14(
|
|
4763
5196
|
"div",
|
|
4764
5197
|
{
|
|
4765
5198
|
ref: containerRef,
|
|
@@ -4787,8 +5220,8 @@ function YaVideo({
|
|
|
4787
5220
|
strokeLinecap: "round",
|
|
4788
5221
|
strokeLinejoin: "round",
|
|
4789
5222
|
children: [
|
|
4790
|
-
/* @__PURE__ */
|
|
4791
|
-
/* @__PURE__ */
|
|
5223
|
+
/* @__PURE__ */ jsx14("rect", { x: "2", y: "4", width: "15", height: "13", rx: "2", ry: "2" }),
|
|
5224
|
+
/* @__PURE__ */ jsx14("polygon", { points: "22 7 15 12 22 17 22 7", fill: "currentColor" })
|
|
4792
5225
|
]
|
|
4793
5226
|
}
|
|
4794
5227
|
);
|
|
@@ -4817,12 +5250,12 @@ function YaVideo({
|
|
|
4817
5250
|
renderVideo(false),
|
|
4818
5251
|
isSmallVideo ? /* @__PURE__ */ jsxs7(YaTooltip, { anchorRef: containerRef, show: isHovered && !isSelected, children: [
|
|
4819
5252
|
videoIcon,
|
|
4820
|
-
/* @__PURE__ */
|
|
5253
|
+
/* @__PURE__ */ jsx14("span", { children: "Click to edit" })
|
|
4821
5254
|
] }) : (
|
|
4822
5255
|
/* For large videos: show overlay inside the container */
|
|
4823
5256
|
/* @__PURE__ */ jsxs7("div", { className: "ya-video-overlay", children: [
|
|
4824
|
-
/* @__PURE__ */
|
|
4825
|
-
/* @__PURE__ */
|
|
5257
|
+
/* @__PURE__ */ jsx14("div", { className: "ya-video-edit-icon", children: videoIcon }),
|
|
5258
|
+
/* @__PURE__ */ jsx14("span", { className: "ya-video-edit-label", children: "Click to edit" })
|
|
4826
5259
|
] })
|
|
4827
5260
|
)
|
|
4828
5261
|
]
|
|
@@ -4831,13 +5264,13 @@ function YaVideo({
|
|
|
4831
5264
|
}
|
|
4832
5265
|
|
|
4833
5266
|
// src/components/YaEmbed.tsx
|
|
4834
|
-
import { useCallback as
|
|
5267
|
+
import { useCallback as useCallback15, useEffect as useEffect13, useRef as useRef14, useState as useState13 } from "react";
|
|
4835
5268
|
|
|
4836
5269
|
// src/components/ya-embed.css
|
|
4837
5270
|
styleInject('.ya-embed-wrapper {\n position: relative;\n display: block;\n width: 100%;\n}\n.ya-embed-wrapper iframe {\n display: block;\n width: 100%;\n height: 100%;\n}\n.ya-embed-container {\n position: relative;\n display: block;\n width: 100%;\n min-width: 80px;\n min-height: 80px;\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-embed-container iframe {\n display: block;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n.ya-embed-editable {\n cursor: pointer;\n}\n.ya-embed-editable:hover {\n outline: 2px dashed var(--color-primary, #d4a574);\n outline-offset: 4px;\n}\n.ya-embed-selected {\n outline: 3px solid var(--color-primary, #d4a574);\n outline-offset: 4px;\n}\n.ya-embed-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 0.2s ease;\n pointer-events: none;\n border-radius: inherit;\n}\n.ya-embed-editable:hover .ya-embed-overlay {\n opacity: 1;\n}\n.ya-embed-selected .ya-embed-overlay {\n opacity: 0;\n}\n.ya-embed-edit-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: white;\n border-radius: 50%;\n color: #1a1a1a;\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);\n}\n.ya-embed-edit-icon svg {\n width: 24px;\n height: 24px;\n}\n.ya-embed-edit-label {\n color: white;\n font-size: 14px;\n font-weight: 500;\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);\n}\n.ya-embed-placeholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n height: 100%;\n min-height: 120px;\n background: #f3f4f6;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n color: #6b7280;\n font-size: 14px;\n}\n.ya-embed-placeholder img {\n width: 64px;\n height: auto;\n opacity: 0.5;\n}\n@keyframes ya-embed-success {\n 0% {\n outline-color: var(--color-primary, #d4a574);\n }\n 50% {\n outline-color: #22c55e;\n outline-width: 4px;\n }\n 100% {\n outline-color: var(--color-primary, #d4a574);\n outline-width: 2px;\n }\n}\n.ya-embed-success {\n animation: ya-embed-success 0.4s ease;\n}\n.ya-embed-loading::after {\n content: "";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0) 100%);\n background-size: 200% 100%;\n animation: ya-embed-shimmer 1.5s infinite;\n}\n@keyframes ya-embed-shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n.ya-embed-container:focus {\n outline: 3px solid var(--color-primary, #d4a574);\n outline-offset: 4px;\n}\n.ya-embed-container:focus:not(:focus-visible) {\n outline: none;\n}\n.ya-embed-container:focus-visible {\n outline: 3px solid var(--color-primary, #d4a574);\n outline-offset: 4px;\n}\n.ya-embed-small .ya-embed-overlay {\n display: none;\n}\n.ya-embed-twitter {\n min-height: 200px;\n}\n.ya-embed-twitter .twitter-tweet {\n margin: 0 auto !important;\n}\n.ya-embed-wrapper[data-embed-type=spotify],\n.ya-embed-container[data-embed-type=spotify] {\n min-height: 80px;\n}\n.ya-embed-wrapper[data-embed-type=soundcloud],\n.ya-embed-container[data-embed-type=soundcloud] {\n min-height: 166px;\n}\n.ya-embed-wrapper[data-embed-type=instagram] iframe,\n.ya-embed-container[data-embed-type=instagram] iframe {\n min-height: 400px;\n}\n');
|
|
4838
5271
|
|
|
4839
5272
|
// src/components/YaEmbed.tsx
|
|
4840
|
-
import { jsx as
|
|
5273
|
+
import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4841
5274
|
function parseEmbedUrl(url) {
|
|
4842
5275
|
if (!url) return null;
|
|
4843
5276
|
const trimmedUrl = url.trim();
|
|
@@ -4950,20 +5383,21 @@ function YaEmbed({
|
|
|
4950
5383
|
loading = "lazy",
|
|
4951
5384
|
defaultValue
|
|
4952
5385
|
}) {
|
|
4953
|
-
const
|
|
4954
|
-
const
|
|
4955
|
-
const
|
|
4956
|
-
const [
|
|
4957
|
-
const [
|
|
4958
|
-
const [
|
|
4959
|
-
const
|
|
5386
|
+
const content = useContent(fieldId);
|
|
5387
|
+
const { mode } = content;
|
|
5388
|
+
const containerRef = useRef14(null);
|
|
5389
|
+
const [isSelected, setIsSelected] = useState13(false);
|
|
5390
|
+
const [isHovered, setIsHovered] = useState13(false);
|
|
5391
|
+
const [isSmallEmbed, setIsSmallEmbed] = useState13(false);
|
|
5392
|
+
const [isInView, setIsInView] = useState13(loading === "eager");
|
|
5393
|
+
const rawValue = content.get();
|
|
4960
5394
|
const parsedValue = parseEmbedValue(rawValue);
|
|
4961
5395
|
const embedData = parsedValue.src ? parsedValue : defaultValue || parsedValue;
|
|
4962
5396
|
const src = embedData.src || "";
|
|
4963
5397
|
const embedType = embedData.type || "custom";
|
|
4964
5398
|
const height = embedData.height;
|
|
4965
5399
|
const aspectRatio = embedData.aspectRatio || propAspectRatio || "16/9";
|
|
4966
|
-
|
|
5400
|
+
useEffect13(() => {
|
|
4967
5401
|
if (loading === "eager" || isInView) return;
|
|
4968
5402
|
const observer = new IntersectionObserver(
|
|
4969
5403
|
(entries) => {
|
|
@@ -4980,7 +5414,7 @@ function YaEmbed({
|
|
|
4980
5414
|
}
|
|
4981
5415
|
return () => observer.disconnect();
|
|
4982
5416
|
}, [loading, isInView]);
|
|
4983
|
-
const handleClick =
|
|
5417
|
+
const handleClick = useCallback15(() => {
|
|
4984
5418
|
if (mode !== "inline-edit") return;
|
|
4985
5419
|
if (document.body.classList.contains("builder-selector-active")) return;
|
|
4986
5420
|
setIsSelected(true);
|
|
@@ -5000,7 +5434,7 @@ function YaEmbed({
|
|
|
5000
5434
|
"*"
|
|
5001
5435
|
);
|
|
5002
5436
|
}, [mode, fieldId, embedData]);
|
|
5003
|
-
|
|
5437
|
+
useEffect13(() => {
|
|
5004
5438
|
if (mode !== "inline-edit") return;
|
|
5005
5439
|
const handleMessage2 = (event) => {
|
|
5006
5440
|
if (event.data?.type === "YA_EMBED_EDIT_COMPLETE" && event.data.fieldId === fieldId) {
|
|
@@ -5013,7 +5447,7 @@ function YaEmbed({
|
|
|
5013
5447
|
window.addEventListener("message", handleMessage2);
|
|
5014
5448
|
return () => window.removeEventListener("message", handleMessage2);
|
|
5015
5449
|
}, [mode, fieldId]);
|
|
5016
|
-
|
|
5450
|
+
useEffect13(() => {
|
|
5017
5451
|
if (mode !== "inline-edit") return;
|
|
5018
5452
|
const checkSize = () => {
|
|
5019
5453
|
if (containerRef.current) {
|
|
@@ -5025,7 +5459,7 @@ function YaEmbed({
|
|
|
5025
5459
|
window.addEventListener("resize", checkSize);
|
|
5026
5460
|
return () => window.removeEventListener("resize", checkSize);
|
|
5027
5461
|
}, [mode]);
|
|
5028
|
-
|
|
5462
|
+
useEffect13(() => {
|
|
5029
5463
|
if (!isSelected || mode !== "inline-edit") return;
|
|
5030
5464
|
let lastRectKey = "";
|
|
5031
5465
|
let lastTime = 0;
|
|
@@ -5062,16 +5496,16 @@ function YaEmbed({
|
|
|
5062
5496
|
const renderEmbed = (isReadOnly) => {
|
|
5063
5497
|
if (!src && !isReadOnly) {
|
|
5064
5498
|
return /* @__PURE__ */ jsxs8("div", { className: "ya-embed-placeholder", children: [
|
|
5065
|
-
/* @__PURE__ */
|
|
5066
|
-
/* @__PURE__ */
|
|
5499
|
+
/* @__PURE__ */ jsx15("img", { src: PLACEHOLDER_SVG3, alt: "" }),
|
|
5500
|
+
/* @__PURE__ */ jsx15("span", { children: "No embed selected" })
|
|
5067
5501
|
] });
|
|
5068
5502
|
}
|
|
5069
5503
|
if (!isInView && loading === "lazy" && !isReadOnly) {
|
|
5070
|
-
return /* @__PURE__ */
|
|
5504
|
+
return /* @__PURE__ */ jsx15("div", { className: "ya-embed-placeholder", style: { aspectRatio }, children: /* @__PURE__ */ jsx15("img", { src: PLACEHOLDER_SVG3, alt: "" }) });
|
|
5071
5505
|
}
|
|
5072
5506
|
if (embedType === "spotify" && src) {
|
|
5073
5507
|
const embedUrl = buildSpotifyEmbedUrl(src);
|
|
5074
|
-
return /* @__PURE__ */
|
|
5508
|
+
return /* @__PURE__ */ jsx15(
|
|
5075
5509
|
"iframe",
|
|
5076
5510
|
{
|
|
5077
5511
|
src: embedUrl,
|
|
@@ -5089,7 +5523,7 @@ function YaEmbed({
|
|
|
5089
5523
|
}
|
|
5090
5524
|
if (embedType === "soundcloud" && src) {
|
|
5091
5525
|
const embedUrl = buildSoundCloudEmbedUrl(src);
|
|
5092
|
-
return /* @__PURE__ */
|
|
5526
|
+
return /* @__PURE__ */ jsx15(
|
|
5093
5527
|
"iframe",
|
|
5094
5528
|
{
|
|
5095
5529
|
src: embedUrl,
|
|
@@ -5106,13 +5540,13 @@ function YaEmbed({
|
|
|
5106
5540
|
}
|
|
5107
5541
|
if (embedType === "twitter" && src) {
|
|
5108
5542
|
return /* @__PURE__ */ jsxs8("div", { className: "ya-embed-twitter", children: [
|
|
5109
|
-
/* @__PURE__ */
|
|
5110
|
-
/* @__PURE__ */
|
|
5543
|
+
/* @__PURE__ */ jsx15("blockquote", { className: "twitter-tweet", "data-dnt": "true", children: /* @__PURE__ */ jsx15("a", { href: embedData.originalUrl || `https://twitter.com/i/status/${src}`, children: "Loading tweet..." }) }),
|
|
5544
|
+
/* @__PURE__ */ jsx15(TwitterWidgetLoader, {})
|
|
5111
5545
|
] });
|
|
5112
5546
|
}
|
|
5113
5547
|
if (embedType === "instagram" && src) {
|
|
5114
5548
|
const embedUrl = buildInstagramEmbedUrl(src);
|
|
5115
|
-
return /* @__PURE__ */
|
|
5549
|
+
return /* @__PURE__ */ jsx15(
|
|
5116
5550
|
"iframe",
|
|
5117
5551
|
{
|
|
5118
5552
|
src: embedUrl,
|
|
@@ -5130,7 +5564,7 @@ function YaEmbed({
|
|
|
5130
5564
|
);
|
|
5131
5565
|
}
|
|
5132
5566
|
if (embedType === "custom" && src) {
|
|
5133
|
-
return /* @__PURE__ */
|
|
5567
|
+
return /* @__PURE__ */ jsx15(
|
|
5134
5568
|
"iframe",
|
|
5135
5569
|
{
|
|
5136
5570
|
src,
|
|
@@ -5154,7 +5588,7 @@ function YaEmbed({
|
|
|
5154
5588
|
maxWidth: maxWidth ? `${maxWidth}px` : void 0
|
|
5155
5589
|
};
|
|
5156
5590
|
if (mode === "read-only") {
|
|
5157
|
-
return /* @__PURE__ */
|
|
5591
|
+
return /* @__PURE__ */ jsx15(
|
|
5158
5592
|
"div",
|
|
5159
5593
|
{
|
|
5160
5594
|
ref: containerRef,
|
|
@@ -5178,8 +5612,8 @@ function YaEmbed({
|
|
|
5178
5612
|
strokeLinecap: "round",
|
|
5179
5613
|
strokeLinejoin: "round",
|
|
5180
5614
|
children: [
|
|
5181
|
-
/* @__PURE__ */
|
|
5182
|
-
/* @__PURE__ */
|
|
5615
|
+
/* @__PURE__ */ jsx15("polyline", { points: "16 18 22 12 16 6" }),
|
|
5616
|
+
/* @__PURE__ */ jsx15("polyline", { points: "8 6 2 12 8 18" })
|
|
5183
5617
|
]
|
|
5184
5618
|
}
|
|
5185
5619
|
);
|
|
@@ -5208,12 +5642,12 @@ function YaEmbed({
|
|
|
5208
5642
|
renderEmbed(false),
|
|
5209
5643
|
isSmallEmbed ? /* @__PURE__ */ jsxs8(YaTooltip, { anchorRef: containerRef, show: isHovered && !isSelected, children: [
|
|
5210
5644
|
embedIcon,
|
|
5211
|
-
/* @__PURE__ */
|
|
5645
|
+
/* @__PURE__ */ jsx15("span", { children: "Click to edit" })
|
|
5212
5646
|
] }) : (
|
|
5213
5647
|
/* For large embeds: show overlay inside the container */
|
|
5214
5648
|
/* @__PURE__ */ jsxs8("div", { className: "ya-embed-overlay", children: [
|
|
5215
|
-
/* @__PURE__ */
|
|
5216
|
-
/* @__PURE__ */
|
|
5649
|
+
/* @__PURE__ */ jsx15("div", { className: "ya-embed-edit-icon", children: embedIcon }),
|
|
5650
|
+
/* @__PURE__ */ jsx15("span", { className: "ya-embed-edit-label", children: "Click to edit" })
|
|
5217
5651
|
] })
|
|
5218
5652
|
)
|
|
5219
5653
|
]
|
|
@@ -5221,7 +5655,7 @@ function YaEmbed({
|
|
|
5221
5655
|
);
|
|
5222
5656
|
}
|
|
5223
5657
|
function TwitterWidgetLoader() {
|
|
5224
|
-
|
|
5658
|
+
useEffect13(() => {
|
|
5225
5659
|
if (window.twttr?.widgets) {
|
|
5226
5660
|
;
|
|
5227
5661
|
window.twttr.widgets.load();
|
|
@@ -5242,7 +5676,7 @@ function TwitterWidgetLoader() {
|
|
|
5242
5676
|
}
|
|
5243
5677
|
|
|
5244
5678
|
// src/components/YaLink.tsx
|
|
5245
|
-
import { useEffect as
|
|
5679
|
+
import { useEffect as useEffect16, useLayoutEffect as useLayoutEffect3, useRef as useRef17, useState as useState16, useCallback as useCallback18, useId } from "react";
|
|
5246
5680
|
import { createPortal as createPortal6 } from "react-dom";
|
|
5247
5681
|
import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
|
|
5248
5682
|
import { BubbleMenu } from "@tiptap/react/menus";
|
|
@@ -5252,7 +5686,7 @@ import { Extension as Extension2 } from "@tiptap/core";
|
|
|
5252
5686
|
import { Link as WouterLink, useLocation } from "wouter";
|
|
5253
5687
|
|
|
5254
5688
|
// src/components/SafeTriangleBelow.tsx
|
|
5255
|
-
import { useEffect as
|
|
5689
|
+
import { useEffect as useEffect14, useState as useState14, useRef as useRef15, useCallback as useCallback16 } from "react";
|
|
5256
5690
|
function SafeTriangleBelow({
|
|
5257
5691
|
triggerRef,
|
|
5258
5692
|
popoverRef,
|
|
@@ -5260,10 +5694,10 @@ function SafeTriangleBelow({
|
|
|
5260
5694
|
onLeave,
|
|
5261
5695
|
onStayInside
|
|
5262
5696
|
}) {
|
|
5263
|
-
const [bounds, setBounds] =
|
|
5264
|
-
const boundsRef =
|
|
5697
|
+
const [bounds, setBounds] = useState14(null);
|
|
5698
|
+
const boundsRef = useRef15(bounds);
|
|
5265
5699
|
boundsRef.current = bounds;
|
|
5266
|
-
|
|
5700
|
+
useEffect14(() => {
|
|
5267
5701
|
if (!isVisible || !triggerRef.current || !popoverRef.current) {
|
|
5268
5702
|
setBounds(null);
|
|
5269
5703
|
return;
|
|
@@ -5281,7 +5715,7 @@ function SafeTriangleBelow({
|
|
|
5281
5715
|
}, 10);
|
|
5282
5716
|
return () => clearTimeout(timer);
|
|
5283
5717
|
}, [isVisible, triggerRef, popoverRef]);
|
|
5284
|
-
const checkMousePosition =
|
|
5718
|
+
const checkMousePosition = useCallback16((e) => {
|
|
5285
5719
|
const b = boundsRef.current;
|
|
5286
5720
|
if (!b) return;
|
|
5287
5721
|
const { clientX: x, clientY: y } = e;
|
|
@@ -5293,7 +5727,7 @@ function SafeTriangleBelow({
|
|
|
5293
5727
|
onStayInside?.();
|
|
5294
5728
|
}
|
|
5295
5729
|
}, [onLeave, onStayInside]);
|
|
5296
|
-
|
|
5730
|
+
useEffect14(() => {
|
|
5297
5731
|
if (!isVisible || !bounds) return;
|
|
5298
5732
|
document.addEventListener("mousemove", checkMousePosition);
|
|
5299
5733
|
return () => document.removeEventListener("mousemove", checkMousePosition);
|
|
@@ -5302,22 +5736,22 @@ function SafeTriangleBelow({
|
|
|
5302
5736
|
}
|
|
5303
5737
|
|
|
5304
5738
|
// src/hooks/useSafeTriangle.ts
|
|
5305
|
-
import { useState as
|
|
5739
|
+
import { useState as useState15, useRef as useRef16, useCallback as useCallback17, useEffect as useEffect15 } from "react";
|
|
5306
5740
|
function useSafeTriangle(options = {}) {
|
|
5307
5741
|
const { showDelay = 0, hideDelay = 150, enabled = true } = options;
|
|
5308
|
-
const [isVisible, setIsVisible] =
|
|
5309
|
-
const [isHovering, setIsHovering] =
|
|
5310
|
-
const triggerRef =
|
|
5311
|
-
const popoverRef =
|
|
5312
|
-
const showTimeoutRef =
|
|
5313
|
-
const hideTimeoutRef =
|
|
5314
|
-
|
|
5742
|
+
const [isVisible, setIsVisible] = useState15(false);
|
|
5743
|
+
const [isHovering, setIsHovering] = useState15(false);
|
|
5744
|
+
const triggerRef = useRef16(null);
|
|
5745
|
+
const popoverRef = useRef16(null);
|
|
5746
|
+
const showTimeoutRef = useRef16(null);
|
|
5747
|
+
const hideTimeoutRef = useRef16(null);
|
|
5748
|
+
useEffect15(() => {
|
|
5315
5749
|
return () => {
|
|
5316
5750
|
if (showTimeoutRef.current) clearTimeout(showTimeoutRef.current);
|
|
5317
5751
|
if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
|
|
5318
5752
|
};
|
|
5319
5753
|
}, []);
|
|
5320
|
-
const show =
|
|
5754
|
+
const show = useCallback17(() => {
|
|
5321
5755
|
if (!enabled) return;
|
|
5322
5756
|
if (hideTimeoutRef.current) {
|
|
5323
5757
|
clearTimeout(hideTimeoutRef.current);
|
|
@@ -5325,7 +5759,7 @@ function useSafeTriangle(options = {}) {
|
|
|
5325
5759
|
}
|
|
5326
5760
|
setIsVisible(true);
|
|
5327
5761
|
}, [enabled]);
|
|
5328
|
-
const hide =
|
|
5762
|
+
const hide = useCallback17(() => {
|
|
5329
5763
|
if (showTimeoutRef.current) {
|
|
5330
5764
|
clearTimeout(showTimeoutRef.current);
|
|
5331
5765
|
showTimeoutRef.current = null;
|
|
@@ -5333,7 +5767,7 @@ function useSafeTriangle(options = {}) {
|
|
|
5333
5767
|
setIsVisible(false);
|
|
5334
5768
|
setIsHovering(false);
|
|
5335
5769
|
}, []);
|
|
5336
|
-
const handleMouseEnter =
|
|
5770
|
+
const handleMouseEnter = useCallback17(() => {
|
|
5337
5771
|
if (!enabled) return;
|
|
5338
5772
|
setIsHovering(true);
|
|
5339
5773
|
if (hideTimeoutRef.current) {
|
|
@@ -5348,7 +5782,7 @@ function useSafeTriangle(options = {}) {
|
|
|
5348
5782
|
setIsVisible(true);
|
|
5349
5783
|
}
|
|
5350
5784
|
}, [showDelay, enabled]);
|
|
5351
|
-
const handleMouseLeave =
|
|
5785
|
+
const handleMouseLeave = useCallback17(() => {
|
|
5352
5786
|
setIsHovering(false);
|
|
5353
5787
|
if (showTimeoutRef.current) {
|
|
5354
5788
|
clearTimeout(showTimeoutRef.current);
|
|
@@ -5358,16 +5792,16 @@ function useSafeTriangle(options = {}) {
|
|
|
5358
5792
|
setIsVisible(false);
|
|
5359
5793
|
}, hideDelay);
|
|
5360
5794
|
}, [hideDelay]);
|
|
5361
|
-
const handleFocus =
|
|
5795
|
+
const handleFocus = useCallback17(() => {
|
|
5362
5796
|
if (!enabled) return;
|
|
5363
5797
|
setIsVisible(true);
|
|
5364
5798
|
}, [enabled]);
|
|
5365
|
-
const handleTriangleLeave =
|
|
5799
|
+
const handleTriangleLeave = useCallback17(() => {
|
|
5366
5800
|
if (!isHovering) {
|
|
5367
5801
|
setIsVisible(false);
|
|
5368
5802
|
}
|
|
5369
5803
|
}, [isHovering]);
|
|
5370
|
-
const handleStayInside =
|
|
5804
|
+
const handleStayInside = useCallback17(() => {
|
|
5371
5805
|
if (hideTimeoutRef.current) {
|
|
5372
5806
|
clearTimeout(hideTimeoutRef.current);
|
|
5373
5807
|
hideTimeoutRef.current = null;
|
|
@@ -5398,7 +5832,7 @@ function useSafeTriangle(options = {}) {
|
|
|
5398
5832
|
styleInject('.ya-link-wrapper {\n position: relative;\n display: inline;\n}\n.ya-link-editable {\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-link-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: 4px;\n border-radius: 4px;\n}\nbody.builder-selector-active .ya-link-editable:hover {\n outline: none;\n cursor: inherit;\n}\n.ya-link-editing {\n outline: 2px solid var(--color-primary, #D4A574);\n outline-offset: 4px;\n border-radius: 4px;\n position: relative;\n}\n.ya-link-editing .ProseMirror {\n color: #1a1a1a !important;\n caret-color: #1a1a1a;\n}\n.ya-link-editing .ProseMirror p::selection,\n.ya-link-editing .ProseMirror::selection {\n background-color: rgba(212, 165, 116, 0.4);\n color: inherit;\n}\n.ya-link-editing .ProseMirror p::-moz-selection,\n.ya-link-editing .ProseMirror::-moz-selection {\n background-color: rgba(212, 165, 116, 0.4);\n color: inherit;\n}\n.ProseMirror-gapcursor {\n display: none !important;\n}\n.ProseMirror .ProseMirror-dropcursor {\n display: none !important;\n}\nbody.builder-selector-active .ya-link-editable:not(.ya-link-editing)::selection,\nbody.builder-selector-active .ya-link-editable:not(.ya-link-editing) *::selection {\n color: inherit;\n}\nbody.builder-selector-active .ya-link-editable:not(.ya-link-editing)::-moz-selection,\nbody.builder-selector-active .ya-link-editable:not(.ya-link-editing) *::-moz-selection {\n color: inherit;\n}\n.ya-link-actions {\n display: flex;\n gap: 8px;\n z-index: 9999;\n background: rgba(26, 26, 26, 0.95);\n padding: 8px 10px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n}\n.ya-link-btn {\n padding: 6px 14px;\n font-size: 12px;\n font-weight: 500;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n border: none;\n}\n.ya-link-btn-cancel {\n background: #333333;\n color: #ffffff;\n border: 1px solid #555555;\n}\n.ya-link-btn-cancel:hover {\n background: #444444;\n color: #ffffff;\n border-color: #666666;\n}\n.ya-link-btn-save {\n background: #D4A574;\n color: #1a1a1a;\n}\n.ya-link-btn-save:hover {\n background: #c4956a;\n}\n.ya-href-popover {\n position: fixed;\n z-index: 10000;\n min-width: 280px;\n max-width: 320px;\n background: #1a1a1a;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\n animation: ya-href-popover-fade-in 0.15s ease;\n overflow: hidden;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n}\n@keyframes ya-href-popover-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n.ya-href-popover::before {\n content: "";\n position: absolute;\n top: -6px;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-bottom: 8px solid #1a1a1a;\n}\n.ya-href-popover-header {\n padding: 12px 16px;\n font-size: 13px;\n font-weight: 600;\n color: #ffffff;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n.ya-href-popover-section {\n padding: 12px 16px;\n}\n.ya-href-popover-label {\n display: block;\n font-size: 11px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.6);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n}\n.ya-href-collapsible-header {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 0;\n background: transparent;\n border: none;\n cursor: pointer;\n transition: color 0.15s ease;\n}\n.ya-href-collapsible-header:hover {\n color: rgba(255, 255, 255, 0.8);\n}\n.ya-href-chevron {\n font-size: 8px;\n color: rgba(255, 255, 255, 0.4);\n}\n.ya-href-popover-pages {\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 200px;\n overflow-y: auto;\n}\n.ya-href-page-btn {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n width: 100%;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid transparent;\n border-radius: 8px;\n color: #e0e0e0;\n font-size: 13px;\n font-weight: 500;\n text-align: left;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.ya-href-page-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n border-color: rgba(255, 255, 255, 0.2);\n}\n.ya-href-page-btn.is-selected {\n background: #D4A574;\n color: #1a1a1a;\n}\n.ya-href-page-btn.is-selected .ya-href-page-path {\n color: rgba(26, 26, 26, 0.6);\n}\n.ya-href-page-path {\n font-size: 11px;\n color: rgba(255, 255, 255, 0.4);\n font-family: monospace;\n word-break: break-all;\n}\n.ya-href-external-toggle {\n display: block;\n width: 100%;\n padding: 10px 16px;\n background: transparent;\n border: none;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n color: #D4A574;\n font-size: 12px;\n font-weight: 500;\n text-align: center;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n.ya-href-external-toggle:hover {\n background: rgba(255, 255, 255, 0.05);\n}\n.ya-href-url-input {\n width: 100%;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n color: #ffffff;\n font-size: 13px;\n outline: none;\n transition: border-color 0.15s ease;\n}\n.ya-href-url-input::placeholder {\n color: rgba(255, 255, 255, 0.4);\n}\n.ya-href-url-input:focus {\n border-color: var(--color-primary, #D4A574);\n}\n.ya-href-popover-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 16px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n}\n.ya-href-popover--above {\n animation: ya-href-popover-fade-in-above 0.15s ease;\n}\n@keyframes ya-href-popover-fade-in-above {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n.ya-href-popover--above::before {\n top: auto;\n bottom: -6px;\n border-bottom: none;\n border-top: 8px solid #1a1a1a;\n}\n.ya-link-edit-popover {\n position: fixed;\n z-index: 10000;\n background: #2a2a2a;\n border-radius: 6px;\n padding: 4px;\n display: flex;\n gap: 4px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n transition: opacity 100ms ease;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n white-space: nowrap;\n}\n.ya-link-edit-popover::before {\n content: "";\n position: absolute;\n top: -5px;\n left: 50%;\n transform: translateX(-50%);\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-bottom: 6px solid #2a2a2a;\n}\n.ya-link-edit-popover button {\n background: #3a3a3a;\n border: none;\n color: #fff;\n padding: 6px 12px;\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n transition: background 0.15s ease;\n}\n.ya-link-edit-popover button:hover {\n background: #4a4a4a;\n}\n');
|
|
5399
5833
|
|
|
5400
5834
|
// src/components/YaLink.tsx
|
|
5401
|
-
import { Fragment as Fragment4, jsx as
|
|
5835
|
+
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
5402
5836
|
function isInternalPath(path) {
|
|
5403
5837
|
if (!path) return false;
|
|
5404
5838
|
if (path.startsWith("#")) return false;
|
|
@@ -5496,37 +5930,40 @@ function discoverSectionsFromDOM() {
|
|
|
5496
5930
|
return sections;
|
|
5497
5931
|
}
|
|
5498
5932
|
function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName, style, as: Component = "a", children, availablePages, onClick, target, rel }) {
|
|
5499
|
-
const { getValue, setValue, mode, saveToWorker, getPages } = useContentStore();
|
|
5500
|
-
const [, navigate] = useLocation();
|
|
5501
|
-
const pages = availablePages ?? getPages();
|
|
5502
|
-
const [sections, setSections] = useState15([]);
|
|
5503
|
-
const [sectionsExpanded, setSectionsExpanded] = useState15(false);
|
|
5504
5933
|
const textFieldId = `${fieldId}.text`;
|
|
5505
5934
|
const hrefFieldId = `${fieldId}.href`;
|
|
5506
|
-
const
|
|
5507
|
-
const
|
|
5935
|
+
const textContent = useContent(textFieldId);
|
|
5936
|
+
const hrefContent = useContent(hrefFieldId);
|
|
5937
|
+
const { mode } = textContent;
|
|
5938
|
+
const { getPages } = useContentStore();
|
|
5939
|
+
const [, navigate] = useLocation();
|
|
5940
|
+
const pages = availablePages ?? getPages();
|
|
5941
|
+
const [sections, setSections] = useState16([]);
|
|
5942
|
+
const [sectionsExpanded, setSectionsExpanded] = useState16(false);
|
|
5943
|
+
const storeText = textContent.get();
|
|
5944
|
+
const storeHref = hrefContent.get();
|
|
5508
5945
|
const isIconMode = children != null && typeof children !== "string";
|
|
5509
5946
|
const text2 = storeText || (typeof children === "string" ? children : "");
|
|
5510
5947
|
const href = storeHref || defaultHref;
|
|
5511
5948
|
const isExternal = isExternalHref(href);
|
|
5512
5949
|
const effectiveTarget = target ?? (isExternal ? "_blank" : void 0);
|
|
5513
5950
|
const effectiveRel = rel ?? (isExternal ? "noopener noreferrer" : void 0);
|
|
5514
|
-
const [editingMode, setEditingMode] =
|
|
5515
|
-
const [originalText, setOriginalText] =
|
|
5516
|
-
const [originalHref, setOriginalHref] =
|
|
5517
|
-
const [currentHref, setCurrentHref] =
|
|
5518
|
-
const [isExternalUrl, setIsExternalUrl] =
|
|
5519
|
-
const [externalUrl, setExternalUrl] =
|
|
5520
|
-
const containerRef =
|
|
5521
|
-
const hrefPopoverRef =
|
|
5522
|
-
const [actionButtonsPos, setActionButtonsPos] =
|
|
5523
|
-
const [editPopoverPos, setEditPopoverPos] =
|
|
5524
|
-
const [editPopoverVisible, setEditPopoverVisible] =
|
|
5525
|
-
const [editPopoverMounted, setEditPopoverMounted] =
|
|
5526
|
-
const [hrefPopoverPos, setHrefPopoverPos] =
|
|
5527
|
-
const handleSaveTextRef =
|
|
5951
|
+
const [editingMode, setEditingMode] = useState16(null);
|
|
5952
|
+
const [originalText, setOriginalText] = useState16(text2);
|
|
5953
|
+
const [originalHref, setOriginalHref] = useState16(href);
|
|
5954
|
+
const [currentHref, setCurrentHref] = useState16(href);
|
|
5955
|
+
const [isExternalUrl, setIsExternalUrl] = useState16(false);
|
|
5956
|
+
const [externalUrl, setExternalUrl] = useState16("");
|
|
5957
|
+
const containerRef = useRef17(null);
|
|
5958
|
+
const hrefPopoverRef = useRef17(null);
|
|
5959
|
+
const [actionButtonsPos, setActionButtonsPos] = useState16(null);
|
|
5960
|
+
const [editPopoverPos, setEditPopoverPos] = useState16(null);
|
|
5961
|
+
const [editPopoverVisible, setEditPopoverVisible] = useState16(false);
|
|
5962
|
+
const [editPopoverMounted, setEditPopoverMounted] = useState16(false);
|
|
5963
|
+
const [hrefPopoverPos, setHrefPopoverPos] = useState16(null);
|
|
5964
|
+
const handleSaveTextRef = useRef17(() => {
|
|
5528
5965
|
});
|
|
5529
|
-
const handleCancelTextRef =
|
|
5966
|
+
const handleCancelTextRef = useRef17(() => {
|
|
5530
5967
|
});
|
|
5531
5968
|
const {
|
|
5532
5969
|
popoverRef: editPopoverRef,
|
|
@@ -5540,12 +5977,12 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5540
5977
|
});
|
|
5541
5978
|
const triggerRef = containerRef;
|
|
5542
5979
|
const instanceId = useId();
|
|
5543
|
-
|
|
5980
|
+
useEffect16(() => {
|
|
5544
5981
|
if (showEditPopover && mode === "inline-edit" && !editingMode) {
|
|
5545
5982
|
window.dispatchEvent(new CustomEvent("yalink:popover-open", { detail: { id: instanceId } }));
|
|
5546
5983
|
}
|
|
5547
5984
|
}, [showEditPopover, mode, editingMode, instanceId]);
|
|
5548
|
-
|
|
5985
|
+
useEffect16(() => {
|
|
5549
5986
|
const handleOtherPopoverOpen = (event) => {
|
|
5550
5987
|
const customEvent = event;
|
|
5551
5988
|
if (customEvent.detail.id !== instanceId && showEditPopover) {
|
|
@@ -5599,19 +6036,19 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5599
6036
|
}
|
|
5600
6037
|
}
|
|
5601
6038
|
});
|
|
5602
|
-
|
|
6039
|
+
useEffect16(() => {
|
|
5603
6040
|
if (editor && editingMode !== "text") {
|
|
5604
6041
|
if (editor.getHTML() !== text2) {
|
|
5605
6042
|
editor.commands.setContent(text2);
|
|
5606
6043
|
}
|
|
5607
6044
|
}
|
|
5608
6045
|
}, [text2, editor, editingMode]);
|
|
5609
|
-
|
|
6046
|
+
useEffect16(() => {
|
|
5610
6047
|
if (editingMode !== "link") {
|
|
5611
6048
|
setCurrentHref(href);
|
|
5612
6049
|
}
|
|
5613
6050
|
}, [href, editingMode]);
|
|
5614
|
-
|
|
6051
|
+
useEffect16(() => {
|
|
5615
6052
|
if (editingMode !== "text" || !containerRef.current) {
|
|
5616
6053
|
setActionButtonsPos(null);
|
|
5617
6054
|
return;
|
|
@@ -5632,7 +6069,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5632
6069
|
window.removeEventListener("resize", updatePosition);
|
|
5633
6070
|
};
|
|
5634
6071
|
}, [editingMode]);
|
|
5635
|
-
|
|
6072
|
+
useEffect16(() => {
|
|
5636
6073
|
const shouldShow = showEditPopover && !editingMode && mode === "inline-edit";
|
|
5637
6074
|
if (shouldShow) {
|
|
5638
6075
|
setEditPopoverMounted(true);
|
|
@@ -5694,7 +6131,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5694
6131
|
window.removeEventListener("resize", updatePosition);
|
|
5695
6132
|
};
|
|
5696
6133
|
}, [editingMode]);
|
|
5697
|
-
|
|
6134
|
+
useEffect16(() => {
|
|
5698
6135
|
if (editingMode !== "link") return;
|
|
5699
6136
|
const handleClickOutside = (event) => {
|
|
5700
6137
|
const target2 = event.target;
|
|
@@ -5708,38 +6145,38 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5708
6145
|
document.addEventListener("mousedown", handleClickOutside);
|
|
5709
6146
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
5710
6147
|
}, [editingMode, originalHref]);
|
|
5711
|
-
const handleSaveText =
|
|
6148
|
+
const handleSaveText = useCallback18(() => {
|
|
5712
6149
|
if (!editor) return;
|
|
5713
6150
|
let html = editor.getHTML();
|
|
5714
6151
|
html = html.replace(/<\/p><p>/g, "<br><br>").replace(/^<p>/, "").replace(/<\/p>$/, "");
|
|
5715
|
-
|
|
5716
|
-
|
|
6152
|
+
textContent.set(html, "user");
|
|
6153
|
+
textContent.save();
|
|
5717
6154
|
setEditingMode(null);
|
|
5718
|
-
}, [editor,
|
|
5719
|
-
const handleSaveLink =
|
|
5720
|
-
|
|
5721
|
-
|
|
6155
|
+
}, [editor, textContent]);
|
|
6156
|
+
const handleSaveLink = useCallback18(() => {
|
|
6157
|
+
hrefContent.set(currentHref, "user");
|
|
6158
|
+
hrefContent.save();
|
|
5722
6159
|
setEditingMode(null);
|
|
5723
6160
|
setIsExternalUrl(false);
|
|
5724
6161
|
setExternalUrl("");
|
|
5725
|
-
}, [
|
|
5726
|
-
const handleCancelText =
|
|
6162
|
+
}, [currentHref, hrefContent]);
|
|
6163
|
+
const handleCancelText = useCallback18(() => {
|
|
5727
6164
|
if (editor) {
|
|
5728
6165
|
editor.commands.setContent(originalText);
|
|
5729
6166
|
}
|
|
5730
6167
|
setEditingMode(null);
|
|
5731
6168
|
}, [editor, originalText]);
|
|
5732
|
-
const handleCancelLink =
|
|
6169
|
+
const handleCancelLink = useCallback18(() => {
|
|
5733
6170
|
setCurrentHref(originalHref);
|
|
5734
6171
|
setEditingMode(null);
|
|
5735
6172
|
setIsExternalUrl(false);
|
|
5736
6173
|
setExternalUrl("");
|
|
5737
6174
|
}, [originalHref]);
|
|
5738
|
-
|
|
6175
|
+
useEffect16(() => {
|
|
5739
6176
|
handleSaveTextRef.current = handleSaveText;
|
|
5740
6177
|
handleCancelTextRef.current = handleCancelText;
|
|
5741
6178
|
}, [handleSaveText, handleCancelText]);
|
|
5742
|
-
const handleClick =
|
|
6179
|
+
const handleClick = useCallback18(
|
|
5743
6180
|
(e) => {
|
|
5744
6181
|
const selectModeEnabled = window.__builderSelectModeEnabled;
|
|
5745
6182
|
if (selectModeEnabled) {
|
|
@@ -5771,7 +6208,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5771
6208
|
},
|
|
5772
6209
|
[href, navigate, onClick]
|
|
5773
6210
|
);
|
|
5774
|
-
const startEditText =
|
|
6211
|
+
const startEditText = useCallback18(() => {
|
|
5775
6212
|
hideEditPopover();
|
|
5776
6213
|
if (isIconMode) {
|
|
5777
6214
|
window.dispatchEvent(new CustomEvent("yatext:edit-mode", {
|
|
@@ -5785,14 +6222,14 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5785
6222
|
}, 20);
|
|
5786
6223
|
}
|
|
5787
6224
|
}, [text2, editor, hideEditPopover, isIconMode, fieldId]);
|
|
5788
|
-
const startEditLink =
|
|
6225
|
+
const startEditLink = useCallback18(() => {
|
|
5789
6226
|
hideEditPopover();
|
|
5790
6227
|
setEditingMode("link");
|
|
5791
6228
|
setOriginalHref(href);
|
|
5792
6229
|
setCurrentHref(href);
|
|
5793
6230
|
setSections(discoverSectionsFromDOM());
|
|
5794
6231
|
}, [href, hideEditPopover]);
|
|
5795
|
-
const handleKeyDown =
|
|
6232
|
+
const handleKeyDown = useCallback18(
|
|
5796
6233
|
(event) => {
|
|
5797
6234
|
if (editingMode !== "text") return;
|
|
5798
6235
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
@@ -5813,7 +6250,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5813
6250
|
},
|
|
5814
6251
|
[editingMode, handleSaveText, handleCancelText]
|
|
5815
6252
|
);
|
|
5816
|
-
const handleFontSizeChange =
|
|
6253
|
+
const handleFontSizeChange = useCallback18(
|
|
5817
6254
|
(e) => {
|
|
5818
6255
|
if (!editor) return;
|
|
5819
6256
|
const size = e.target.value;
|
|
@@ -5825,7 +6262,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5825
6262
|
},
|
|
5826
6263
|
[editor]
|
|
5827
6264
|
);
|
|
5828
|
-
const handleFontWeightChange =
|
|
6265
|
+
const handleFontWeightChange = useCallback18(
|
|
5829
6266
|
(e) => {
|
|
5830
6267
|
if (!editor) return;
|
|
5831
6268
|
const weight = e.target.value;
|
|
@@ -5837,11 +6274,11 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5837
6274
|
},
|
|
5838
6275
|
[editor]
|
|
5839
6276
|
);
|
|
5840
|
-
const handlePageSelect =
|
|
6277
|
+
const handlePageSelect = useCallback18((path) => {
|
|
5841
6278
|
setCurrentHref(path);
|
|
5842
6279
|
setIsExternalUrl(false);
|
|
5843
6280
|
}, []);
|
|
5844
|
-
const handleExternalUrlApply =
|
|
6281
|
+
const handleExternalUrlApply = useCallback18(() => {
|
|
5845
6282
|
if (externalUrl) {
|
|
5846
6283
|
setCurrentHref(externalUrl);
|
|
5847
6284
|
}
|
|
@@ -5857,9 +6294,9 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5857
6294
|
return attrs.fontWeight || "";
|
|
5858
6295
|
};
|
|
5859
6296
|
if (mode === "read-only") {
|
|
5860
|
-
const content = isIconMode ? children : /* @__PURE__ */
|
|
6297
|
+
const content = isIconMode ? children : /* @__PURE__ */ jsx16(SafeHtml, { content: text2, mode });
|
|
5861
6298
|
if (isInternalPath(href)) {
|
|
5862
|
-
return /* @__PURE__ */
|
|
6299
|
+
return /* @__PURE__ */ jsx16(
|
|
5863
6300
|
WouterLink,
|
|
5864
6301
|
{
|
|
5865
6302
|
href,
|
|
@@ -5871,7 +6308,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5871
6308
|
}
|
|
5872
6309
|
);
|
|
5873
6310
|
}
|
|
5874
|
-
return /* @__PURE__ */
|
|
6311
|
+
return /* @__PURE__ */ jsx16(
|
|
5875
6312
|
Component,
|
|
5876
6313
|
{
|
|
5877
6314
|
ref: containerRef,
|
|
@@ -5887,7 +6324,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5887
6324
|
);
|
|
5888
6325
|
}
|
|
5889
6326
|
return /* @__PURE__ */ jsxs9("span", { className: `ya-link-wrapper ${wrapperClassName || ""}`, children: [
|
|
5890
|
-
/* @__PURE__ */
|
|
6327
|
+
/* @__PURE__ */ jsx16(
|
|
5891
6328
|
Component,
|
|
5892
6329
|
{
|
|
5893
6330
|
ref: containerRef,
|
|
@@ -5916,27 +6353,27 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5916
6353
|
options: { offset: 6, placement: "top" },
|
|
5917
6354
|
className: "ya-bubble-menu",
|
|
5918
6355
|
children: [
|
|
5919
|
-
/* @__PURE__ */
|
|
6356
|
+
/* @__PURE__ */ jsx16(
|
|
5920
6357
|
"button",
|
|
5921
6358
|
{
|
|
5922
6359
|
type: "button",
|
|
5923
6360
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
5924
6361
|
className: `ya-bubble-btn ${editor.isActive("bold") ? "is-active" : ""}`,
|
|
5925
6362
|
title: "Bold",
|
|
5926
|
-
children: /* @__PURE__ */
|
|
6363
|
+
children: /* @__PURE__ */ jsx16("strong", { children: "B" })
|
|
5927
6364
|
}
|
|
5928
6365
|
),
|
|
5929
|
-
/* @__PURE__ */
|
|
6366
|
+
/* @__PURE__ */ jsx16(
|
|
5930
6367
|
"button",
|
|
5931
6368
|
{
|
|
5932
6369
|
type: "button",
|
|
5933
6370
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
5934
6371
|
className: `ya-bubble-btn ${editor.isActive("italic") ? "is-active" : ""}`,
|
|
5935
6372
|
title: "Italic",
|
|
5936
|
-
children: /* @__PURE__ */
|
|
6373
|
+
children: /* @__PURE__ */ jsx16("em", { children: "I" })
|
|
5937
6374
|
}
|
|
5938
6375
|
),
|
|
5939
|
-
/* @__PURE__ */
|
|
6376
|
+
/* @__PURE__ */ jsx16("span", { className: "ya-bubble-divider" }),
|
|
5940
6377
|
/* @__PURE__ */ jsxs9(
|
|
5941
6378
|
"select",
|
|
5942
6379
|
{
|
|
@@ -5945,8 +6382,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5945
6382
|
className: "ya-bubble-select",
|
|
5946
6383
|
title: "Font Size",
|
|
5947
6384
|
children: [
|
|
5948
|
-
/* @__PURE__ */
|
|
5949
|
-
Object.entries(SIZE_PRESETS2).map(([name, size]) => /* @__PURE__ */
|
|
6385
|
+
/* @__PURE__ */ jsx16("option", { value: "", children: "Size" }),
|
|
6386
|
+
Object.entries(SIZE_PRESETS2).map(([name, size]) => /* @__PURE__ */ jsx16("option", { value: size, children: name }, name))
|
|
5950
6387
|
]
|
|
5951
6388
|
}
|
|
5952
6389
|
),
|
|
@@ -5958,8 +6395,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5958
6395
|
className: "ya-bubble-select",
|
|
5959
6396
|
title: "Font Weight",
|
|
5960
6397
|
children: [
|
|
5961
|
-
/* @__PURE__ */
|
|
5962
|
-
Object.entries(WEIGHT_PRESETS2).map(([name, weight]) => /* @__PURE__ */
|
|
6398
|
+
/* @__PURE__ */ jsx16("option", { value: "", children: "Weight" }),
|
|
6399
|
+
Object.entries(WEIGHT_PRESETS2).map(([name, weight]) => /* @__PURE__ */ jsx16("option", { value: weight, children: name }, name))
|
|
5963
6400
|
]
|
|
5964
6401
|
}
|
|
5965
6402
|
)
|
|
@@ -5969,7 +6406,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5969
6406
|
document.body
|
|
5970
6407
|
),
|
|
5971
6408
|
editingMode === "text" ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
5972
|
-
/* @__PURE__ */
|
|
6409
|
+
/* @__PURE__ */ jsx16(EditorContent2, { editor }),
|
|
5973
6410
|
actionButtonsPos && createPortal6(
|
|
5974
6411
|
/* @__PURE__ */ jsxs9(
|
|
5975
6412
|
"div",
|
|
@@ -5981,15 +6418,15 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
5981
6418
|
right: actionButtonsPos.right
|
|
5982
6419
|
},
|
|
5983
6420
|
children: [
|
|
5984
|
-
/* @__PURE__ */
|
|
5985
|
-
/* @__PURE__ */
|
|
6421
|
+
/* @__PURE__ */ jsx16("button", { type: "button", onClick: handleCancelText, className: "ya-link-btn ya-link-btn-cancel", children: "Cancel" }),
|
|
6422
|
+
/* @__PURE__ */ jsx16("button", { type: "button", onClick: handleSaveText, className: "ya-link-btn ya-link-btn-save", children: "Save" })
|
|
5986
6423
|
]
|
|
5987
6424
|
}
|
|
5988
6425
|
),
|
|
5989
6426
|
document.body
|
|
5990
6427
|
)
|
|
5991
|
-
] }) : /* @__PURE__ */
|
|
5992
|
-
] }) : /* @__PURE__ */
|
|
6428
|
+
] }) : /* @__PURE__ */ jsx16(SafeHtml, { content: text2, mode })
|
|
6429
|
+
] }) : /* @__PURE__ */ jsx16(SafeHtml, { content: text2, mode })
|
|
5993
6430
|
}
|
|
5994
6431
|
),
|
|
5995
6432
|
editPopoverMounted && editPopoverPos && createPortal6(
|
|
@@ -6007,14 +6444,14 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
6007
6444
|
onMouseEnter: safeTriangleHandlers.onMouseEnter,
|
|
6008
6445
|
onMouseLeave: safeTriangleHandlers.onMouseLeave,
|
|
6009
6446
|
children: [
|
|
6010
|
-
/* @__PURE__ */
|
|
6011
|
-
/* @__PURE__ */
|
|
6447
|
+
/* @__PURE__ */ jsx16("button", { type: "button", onClick: startEditText, children: "Edit text" }),
|
|
6448
|
+
/* @__PURE__ */ jsx16("button", { type: "button", onClick: startEditLink, children: "Edit link" })
|
|
6012
6449
|
]
|
|
6013
6450
|
}
|
|
6014
6451
|
),
|
|
6015
6452
|
document.body
|
|
6016
6453
|
),
|
|
6017
|
-
/* @__PURE__ */
|
|
6454
|
+
/* @__PURE__ */ jsx16(
|
|
6018
6455
|
SafeTriangleBelow,
|
|
6019
6456
|
{
|
|
6020
6457
|
triggerRef,
|
|
@@ -6037,7 +6474,7 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
6037
6474
|
transform: "translateX(-50%)"
|
|
6038
6475
|
},
|
|
6039
6476
|
children: [
|
|
6040
|
-
/* @__PURE__ */
|
|
6477
|
+
/* @__PURE__ */ jsx16("div", { className: "ya-href-popover-header", children: "Link destination" }),
|
|
6041
6478
|
!isExternalUrl ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
6042
6479
|
sections.length > 0 && /* @__PURE__ */ jsxs9("div", { className: "ya-href-popover-section", children: [
|
|
6043
6480
|
/* @__PURE__ */ jsxs9(
|
|
@@ -6047,14 +6484,14 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
6047
6484
|
className: "ya-href-popover-label ya-href-collapsible-header",
|
|
6048
6485
|
onClick: () => setSectionsExpanded(!sectionsExpanded),
|
|
6049
6486
|
children: [
|
|
6050
|
-
/* @__PURE__ */
|
|
6487
|
+
/* @__PURE__ */ jsx16("span", { className: "ya-href-chevron", children: sectionsExpanded ? "\u25BC" : "\u25B6" }),
|
|
6051
6488
|
"Scroll to section (",
|
|
6052
6489
|
sections.length,
|
|
6053
6490
|
")"
|
|
6054
6491
|
]
|
|
6055
6492
|
}
|
|
6056
6493
|
),
|
|
6057
|
-
sectionsExpanded && /* @__PURE__ */
|
|
6494
|
+
sectionsExpanded && /* @__PURE__ */ jsx16("div", { className: "ya-href-popover-pages", children: sections.map((section) => /* @__PURE__ */ jsxs9(
|
|
6058
6495
|
"button",
|
|
6059
6496
|
{
|
|
6060
6497
|
type: "button",
|
|
@@ -6062,15 +6499,15 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
6062
6499
|
onClick: () => handlePageSelect(section.path),
|
|
6063
6500
|
children: [
|
|
6064
6501
|
section.label,
|
|
6065
|
-
/* @__PURE__ */
|
|
6502
|
+
/* @__PURE__ */ jsx16("span", { className: "ya-href-page-path", children: section.path })
|
|
6066
6503
|
]
|
|
6067
6504
|
},
|
|
6068
6505
|
section.path
|
|
6069
6506
|
)) })
|
|
6070
6507
|
] }),
|
|
6071
6508
|
pages.length > 0 && /* @__PURE__ */ jsxs9("div", { className: "ya-href-popover-section", children: [
|
|
6072
|
-
/* @__PURE__ */
|
|
6073
|
-
/* @__PURE__ */
|
|
6509
|
+
/* @__PURE__ */ jsx16("label", { className: "ya-href-popover-label", children: "Navigate to page" }),
|
|
6510
|
+
/* @__PURE__ */ jsx16("div", { className: "ya-href-popover-pages", children: pages.map((page) => /* @__PURE__ */ jsxs9(
|
|
6074
6511
|
"button",
|
|
6075
6512
|
{
|
|
6076
6513
|
type: "button",
|
|
@@ -6078,13 +6515,13 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
6078
6515
|
onClick: () => handlePageSelect(page.path),
|
|
6079
6516
|
children: [
|
|
6080
6517
|
page.label,
|
|
6081
|
-
/* @__PURE__ */
|
|
6518
|
+
/* @__PURE__ */ jsx16("span", { className: "ya-href-page-path", children: page.path })
|
|
6082
6519
|
]
|
|
6083
6520
|
},
|
|
6084
6521
|
page.path
|
|
6085
6522
|
)) })
|
|
6086
6523
|
] }),
|
|
6087
|
-
/* @__PURE__ */
|
|
6524
|
+
/* @__PURE__ */ jsx16(
|
|
6088
6525
|
"button",
|
|
6089
6526
|
{
|
|
6090
6527
|
type: "button",
|
|
@@ -6098,8 +6535,8 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
6098
6535
|
)
|
|
6099
6536
|
] }) : /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
6100
6537
|
/* @__PURE__ */ jsxs9("div", { className: "ya-href-popover-section", children: [
|
|
6101
|
-
/* @__PURE__ */
|
|
6102
|
-
/* @__PURE__ */
|
|
6538
|
+
/* @__PURE__ */ jsx16("label", { className: "ya-href-popover-label", children: "External URL" }),
|
|
6539
|
+
/* @__PURE__ */ jsx16(
|
|
6103
6540
|
"input",
|
|
6104
6541
|
{
|
|
6105
6542
|
type: "url",
|
|
@@ -6111,11 +6548,11 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
6111
6548
|
}
|
|
6112
6549
|
)
|
|
6113
6550
|
] }),
|
|
6114
|
-
/* @__PURE__ */
|
|
6551
|
+
/* @__PURE__ */ jsx16("button", { type: "button", className: "ya-href-external-toggle", onClick: () => setIsExternalUrl(false), children: "\u2190 Back to pages" })
|
|
6115
6552
|
] }),
|
|
6116
6553
|
/* @__PURE__ */ jsxs9("div", { className: "ya-href-popover-actions", children: [
|
|
6117
|
-
/* @__PURE__ */
|
|
6118
|
-
isExternalUrl ? /* @__PURE__ */
|
|
6554
|
+
/* @__PURE__ */ jsx16("button", { type: "button", className: "ya-link-btn ya-link-btn-cancel", onClick: handleCancelLink, children: "Cancel" }),
|
|
6555
|
+
isExternalUrl ? /* @__PURE__ */ jsx16("button", { type: "button", className: "ya-link-btn ya-link-btn-save", onClick: handleExternalUrlApply, children: "Apply" }) : /* @__PURE__ */ jsx16("button", { type: "button", className: "ya-link-btn ya-link-btn-save", onClick: handleSaveLink, children: "Save" })
|
|
6119
6556
|
] })
|
|
6120
6557
|
]
|
|
6121
6558
|
}
|
|
@@ -6126,34 +6563,34 @@ function YaLink({ fieldId, href: defaultHref = "#", className, wrapperClassName,
|
|
|
6126
6563
|
}
|
|
6127
6564
|
|
|
6128
6565
|
// src/components/YaContainer.tsx
|
|
6129
|
-
import { useCallback as
|
|
6566
|
+
import { useCallback as useCallback20, useEffect as useEffect18, useRef as useRef19, useState as useState18 } from "react";
|
|
6130
6567
|
import { createPortal as createPortal8 } from "react-dom";
|
|
6131
6568
|
|
|
6132
6569
|
// src/components/Tooltip.tsx
|
|
6133
|
-
import { useState as
|
|
6570
|
+
import { useState as useState17, useRef as useRef18, useEffect as useEffect17, useLayoutEffect as useLayoutEffect4, useCallback as useCallback19 } from "react";
|
|
6134
6571
|
import { createPortal as createPortal7 } from "react-dom";
|
|
6135
6572
|
|
|
6136
6573
|
// src/components/tooltip.css
|
|
6137
6574
|
styleInject('.ya-tooltip-trigger {\n display: inline-flex;\n}\n.ya-tooltip-v2 {\n position: fixed;\n z-index: 10000;\n padding: 8px 12px;\n background: #1a1a1a;\n color: white;\n font-size: 13px;\n font-weight: 500;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n border-radius: 6px;\n white-space: nowrap;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n pointer-events: none;\n animation: ya-tooltip-v2-fade-in 0.15s ease-out;\n}\n@keyframes ya-tooltip-v2-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n.ya-tooltip-v2-arrow {\n position: absolute;\n width: 0;\n height: 0;\n left: 50%;\n transform: translateX(-50%);\n}\n.ya-tooltip-v2-bottom .ya-tooltip-v2-arrow {\n top: -6px;\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-bottom: 6px solid #1a1a1a;\n}\n.ya-tooltip-v2-top .ya-tooltip-v2-arrow {\n bottom: -6px;\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-top: 6px solid #1a1a1a;\n}\n.ya-tooltip-v2-left .ya-tooltip-v2-arrow {\n right: -6px;\n left: auto;\n top: 50%;\n transform: translateY(-50%);\n border-top: 6px solid transparent;\n border-bottom: 6px solid transparent;\n border-left: 6px solid #1a1a1a;\n}\n.ya-tooltip-v2-right .ya-tooltip-v2-arrow {\n left: -6px;\n top: 50%;\n transform: translateY(-50%);\n border-top: 6px solid transparent;\n border-bottom: 6px solid transparent;\n border-right: 6px solid #1a1a1a;\n}\n');
|
|
6138
6575
|
|
|
6139
6576
|
// src/components/Tooltip.tsx
|
|
6140
|
-
import { Fragment as Fragment5, jsx as
|
|
6577
|
+
import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
6141
6578
|
function Tooltip({
|
|
6142
6579
|
content,
|
|
6143
6580
|
children,
|
|
6144
6581
|
position = "bottom",
|
|
6145
6582
|
delay = 0
|
|
6146
6583
|
}) {
|
|
6147
|
-
const [isVisible, setIsVisible] =
|
|
6148
|
-
const [tooltipPosition, setTooltipPosition] =
|
|
6149
|
-
const [isMounted, setIsMounted] =
|
|
6150
|
-
const triggerRef =
|
|
6151
|
-
const tooltipRef =
|
|
6152
|
-
const timeoutRef =
|
|
6153
|
-
|
|
6584
|
+
const [isVisible, setIsVisible] = useState17(false);
|
|
6585
|
+
const [tooltipPosition, setTooltipPosition] = useState17(null);
|
|
6586
|
+
const [isMounted, setIsMounted] = useState17(false);
|
|
6587
|
+
const triggerRef = useRef18(null);
|
|
6588
|
+
const tooltipRef = useRef18(null);
|
|
6589
|
+
const timeoutRef = useRef18(null);
|
|
6590
|
+
useEffect17(() => {
|
|
6154
6591
|
setIsMounted(true);
|
|
6155
6592
|
}, []);
|
|
6156
|
-
const calculatePosition =
|
|
6593
|
+
const calculatePosition = useCallback19(() => {
|
|
6157
6594
|
if (!triggerRef.current) return null;
|
|
6158
6595
|
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
6159
6596
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
@@ -6238,7 +6675,7 @@ function Tooltip({
|
|
|
6238
6675
|
setIsVisible(false);
|
|
6239
6676
|
setTooltipPosition(null);
|
|
6240
6677
|
};
|
|
6241
|
-
|
|
6678
|
+
useEffect17(() => {
|
|
6242
6679
|
return () => {
|
|
6243
6680
|
if (timeoutRef.current) {
|
|
6244
6681
|
clearTimeout(timeoutRef.current);
|
|
@@ -6311,7 +6748,7 @@ function Tooltip({
|
|
|
6311
6748
|
role: "tooltip",
|
|
6312
6749
|
children: [
|
|
6313
6750
|
content,
|
|
6314
|
-
/* @__PURE__ */
|
|
6751
|
+
/* @__PURE__ */ jsx17(
|
|
6315
6752
|
"div",
|
|
6316
6753
|
{
|
|
6317
6754
|
className: "ya-tooltip-v2-arrow",
|
|
@@ -6324,7 +6761,7 @@ function Tooltip({
|
|
|
6324
6761
|
document.body
|
|
6325
6762
|
) : null;
|
|
6326
6763
|
return /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
6327
|
-
/* @__PURE__ */
|
|
6764
|
+
/* @__PURE__ */ jsx17(
|
|
6328
6765
|
"div",
|
|
6329
6766
|
{
|
|
6330
6767
|
ref: triggerRef,
|
|
@@ -6342,7 +6779,7 @@ function Tooltip({
|
|
|
6342
6779
|
styleInject('.ya-container {\n position: relative;\n}\n.ya-container-has-overlay::after {\n content: "";\n position: absolute;\n inset: 0;\n background: var(--ya-overlay-color, transparent);\n opacity: var(--ya-overlay-opacity, 0);\n pointer-events: none;\n z-index: 0;\n}\n.ya-container > *:not(.ya-container-toolbar) {\n position: relative;\n z-index: 1;\n}\n.ya-container-editable {\n transition: outline 0.15s ease;\n pointer-events: none;\n}\n.ya-container-editable > * {\n pointer-events: auto;\n}\n.ya-container-editable:hover {\n outline: 2px dashed var(--color-primary, #D4A574);\n outline-offset: -2px;\n}\nbody.builder-selector-active .ya-container-editable:hover {\n outline: none;\n}\n.ya-container-selected {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: -3px;\n}\n.ya-container-toolbar {\n display: flex;\n gap: 4px;\n background: rgba(26, 26, 26, 0.95);\n padding: 6px 8px;\n border-radius: 8px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n z-index: 9999;\n animation: ya-container-toolbar-fade-in 0.15s ease;\n}\n@keyframes ya-container-toolbar-fade-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ya-container-toolbar button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: #3a3a3a;\n border: none;\n border-radius: 6px;\n color: #ffffff;\n cursor: pointer;\n transition: background 0.15s ease, transform 0.1s ease;\n}\n.ya-container-toolbar button:hover {\n background: #4a4a4a;\n transform: scale(1.05);\n}\n.ya-container-toolbar button:active {\n transform: scale(0.98);\n}\n.ya-container-toolbar button.active {\n background: var(--color-primary, #D4A574);\n color: #1a1a1a;\n}\n.ya-container-toolbar button svg {\n width: 18px;\n height: 18px;\n}\n.ya-container-toolbar button[aria-label="Clear background"] {\n background: #3a2a2a;\n}\n.ya-container-toolbar button[aria-label="Clear background"]:hover {\n background: #5a3a3a;\n}\n.ya-container:focus-visible {\n outline: 3px solid var(--color-primary, #D4A574);\n outline-offset: -3px;\n}\n.ya-container-toolbar button:focus-visible {\n outline: 2px solid var(--color-primary, #D4A574);\n outline-offset: 2px;\n}\n.ya-container-drop-target {\n outline: 2px dashed var(--ya-drop-color, #3b82f6) !important;\n outline-offset: -2px;\n pointer-events: auto !important;\n}\n.ya-container-drop-target .ya-container-toolbar {\n display: none !important;\n}\n.ya-container-drop-hover {\n outline: 3px solid var(--ya-drop-color, #3b82f6) !important;\n outline-offset: -3px;\n}\n.ya-container-drop-hover::before {\n content: "";\n position: absolute;\n inset: 0;\n background: rgba(59, 130, 246, 0.08);\n pointer-events: none;\n z-index: 10;\n animation: ya-container-drop-pulse 1s ease-in-out infinite;\n}\n@keyframes ya-container-drop-pulse {\n 0%, 100% {\n background: rgba(59, 130, 246, 0.05);\n }\n 50% {\n background: rgba(59, 130, 246, 0.12);\n }\n}\n.ya-container-tooltip {\n background: #1a1a1a;\n color: white;\n padding: 8px 12px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n white-space: nowrap;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n z-index: 10000;\n pointer-events: none;\n animation: ya-container-tooltip-fade-in 0.1s ease;\n}\n@keyframes ya-container-tooltip-fade-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n');
|
|
6343
6780
|
|
|
6344
6781
|
// src/components/YaContainer.tsx
|
|
6345
|
-
import { jsx as
|
|
6782
|
+
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
6346
6783
|
function parseBackgroundConfig(value) {
|
|
6347
6784
|
if (!value) {
|
|
6348
6785
|
return { type: "none" };
|
|
@@ -6384,8 +6821,8 @@ function deriveContainerLabel(element) {
|
|
|
6384
6821
|
return tagLabels[tagName] || "Section";
|
|
6385
6822
|
}
|
|
6386
6823
|
function Toolbar({ containerRef, onImageClick, onColorClick, onAIClick: _onAIClick, onClearClick, hasBackground }) {
|
|
6387
|
-
const [position, setPosition] =
|
|
6388
|
-
|
|
6824
|
+
const [position, setPosition] = useState18(null);
|
|
6825
|
+
useEffect18(() => {
|
|
6389
6826
|
const updatePosition = () => {
|
|
6390
6827
|
if (containerRef.current) {
|
|
6391
6828
|
const rect = containerRef.current.getBoundingClientRect();
|
|
@@ -6416,40 +6853,40 @@ function Toolbar({ containerRef, onImageClick, onColorClick, onAIClick: _onAICli
|
|
|
6416
6853
|
},
|
|
6417
6854
|
onClick: (e) => e.stopPropagation(),
|
|
6418
6855
|
children: [
|
|
6419
|
-
/* @__PURE__ */
|
|
6856
|
+
/* @__PURE__ */ jsx18(Tooltip, { content: "Background Image", position: "bottom", children: /* @__PURE__ */ jsx18(
|
|
6420
6857
|
"button",
|
|
6421
6858
|
{
|
|
6422
6859
|
type: "button",
|
|
6423
6860
|
onClick: onImageClick,
|
|
6424
6861
|
"aria-label": "Edit background image",
|
|
6425
6862
|
children: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
6426
|
-
/* @__PURE__ */
|
|
6427
|
-
/* @__PURE__ */
|
|
6428
|
-
/* @__PURE__ */
|
|
6863
|
+
/* @__PURE__ */ jsx18("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
6864
|
+
/* @__PURE__ */ jsx18("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
6865
|
+
/* @__PURE__ */ jsx18("polyline", { points: "21 15 16 10 5 21" })
|
|
6429
6866
|
] })
|
|
6430
6867
|
}
|
|
6431
6868
|
) }),
|
|
6432
|
-
/* @__PURE__ */
|
|
6869
|
+
/* @__PURE__ */ jsx18(Tooltip, { content: "Background Color", position: "bottom", children: /* @__PURE__ */ jsx18(
|
|
6433
6870
|
"button",
|
|
6434
6871
|
{
|
|
6435
6872
|
type: "button",
|
|
6436
6873
|
onClick: onColorClick,
|
|
6437
6874
|
"aria-label": "Edit background color",
|
|
6438
6875
|
children: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
6439
|
-
/* @__PURE__ */
|
|
6440
|
-
/* @__PURE__ */
|
|
6876
|
+
/* @__PURE__ */ jsx18("circle", { cx: "12", cy: "12", r: "10" }),
|
|
6877
|
+
/* @__PURE__ */ jsx18("path", { d: "M12 2a10 10 0 0 1 0 20", fill: "currentColor" })
|
|
6441
6878
|
] })
|
|
6442
6879
|
}
|
|
6443
6880
|
) }),
|
|
6444
|
-
hasBackground && /* @__PURE__ */
|
|
6881
|
+
hasBackground && /* @__PURE__ */ jsx18(Tooltip, { content: "Remove Background", position: "bottom", children: /* @__PURE__ */ jsx18(
|
|
6445
6882
|
"button",
|
|
6446
6883
|
{
|
|
6447
6884
|
type: "button",
|
|
6448
6885
|
onClick: onClearClick,
|
|
6449
6886
|
"aria-label": "Clear background",
|
|
6450
6887
|
children: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
6451
|
-
/* @__PURE__ */
|
|
6452
|
-
/* @__PURE__ */
|
|
6888
|
+
/* @__PURE__ */ jsx18("circle", { cx: "12", cy: "12", r: "10" }),
|
|
6889
|
+
/* @__PURE__ */ jsx18("line", { x1: "4.93", y1: "4.93", x2: "19.07", y2: "19.07" })
|
|
6453
6890
|
] })
|
|
6454
6891
|
}
|
|
6455
6892
|
) })
|
|
@@ -6468,13 +6905,13 @@ function YaContainer({
|
|
|
6468
6905
|
defaultBackground
|
|
6469
6906
|
}) {
|
|
6470
6907
|
const { getValue, setValue, saveToWorker, mode } = useContentStore();
|
|
6471
|
-
const containerRef =
|
|
6472
|
-
const [isHovered, setIsHovered] =
|
|
6473
|
-
const [isSelected, setIsSelected] =
|
|
6474
|
-
const [isDropMode, setIsDropMode] =
|
|
6475
|
-
const [isDropHover, setIsDropHover] =
|
|
6476
|
-
const [previewConfig, setPreviewConfig] =
|
|
6477
|
-
|
|
6908
|
+
const containerRef = useRef19(null);
|
|
6909
|
+
const [isHovered, setIsHovered] = useState18(false);
|
|
6910
|
+
const [isSelected, setIsSelected] = useState18(false);
|
|
6911
|
+
const [isDropMode, setIsDropMode] = useState18(false);
|
|
6912
|
+
const [isDropHover, setIsDropHover] = useState18(false);
|
|
6913
|
+
const [previewConfig, setPreviewConfig] = useState18(null);
|
|
6914
|
+
useEffect18(() => {
|
|
6478
6915
|
if (mode !== "inline-edit") return;
|
|
6479
6916
|
const containerEl = containerRef.current;
|
|
6480
6917
|
if (!containerEl) return;
|
|
@@ -6517,7 +6954,7 @@ function YaContainer({
|
|
|
6517
6954
|
overlayCustomProps["--ya-overlay-color"] = displayConfig.overlay.color;
|
|
6518
6955
|
overlayCustomProps["--ya-overlay-opacity"] = displayConfig.overlay.opacity;
|
|
6519
6956
|
}
|
|
6520
|
-
const handleImageClick =
|
|
6957
|
+
const handleImageClick = useCallback20(() => {
|
|
6521
6958
|
if (mode !== "inline-edit") return;
|
|
6522
6959
|
setIsSelected(true);
|
|
6523
6960
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
@@ -6537,7 +6974,7 @@ function YaContainer({
|
|
|
6537
6974
|
"*"
|
|
6538
6975
|
);
|
|
6539
6976
|
}, [mode, fieldId, backgroundConfig]);
|
|
6540
|
-
const handleColorClick =
|
|
6977
|
+
const handleColorClick = useCallback20(() => {
|
|
6541
6978
|
if (mode !== "inline-edit") return;
|
|
6542
6979
|
setIsSelected(true);
|
|
6543
6980
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
@@ -6557,7 +6994,7 @@ function YaContainer({
|
|
|
6557
6994
|
"*"
|
|
6558
6995
|
);
|
|
6559
6996
|
}, [mode, fieldId, backgroundConfig]);
|
|
6560
|
-
const handleAIClick =
|
|
6997
|
+
const handleAIClick = useCallback20(() => {
|
|
6561
6998
|
if (mode !== "inline-edit") return;
|
|
6562
6999
|
const label = deriveContainerLabel(containerRef.current);
|
|
6563
7000
|
window.parent.postMessage(
|
|
@@ -6570,14 +7007,14 @@ function YaContainer({
|
|
|
6570
7007
|
"*"
|
|
6571
7008
|
);
|
|
6572
7009
|
}, [mode, fieldId, backgroundConfig]);
|
|
6573
|
-
const handleClearClick =
|
|
7010
|
+
const handleClearClick = useCallback20(() => {
|
|
6574
7011
|
if (mode !== "inline-edit") return;
|
|
6575
7012
|
const clearedConfig = { type: "none" };
|
|
6576
7013
|
const serialized = serializeBackgroundConfig(clearedConfig);
|
|
6577
7014
|
setValue(fieldId, serialized);
|
|
6578
7015
|
saveToWorker?.(fieldId, serialized);
|
|
6579
7016
|
}, [mode, fieldId, setValue, saveToWorker]);
|
|
6580
|
-
|
|
7017
|
+
useEffect18(() => {
|
|
6581
7018
|
if (mode !== "inline-edit") return;
|
|
6582
7019
|
const handleMessage2 = (event) => {
|
|
6583
7020
|
if (event.data?.type === "YA_CONTAINER_UPDATE_PREVIEW" && event.data.fieldId === fieldId) {
|
|
@@ -6598,7 +7035,7 @@ function YaContainer({
|
|
|
6598
7035
|
window.addEventListener("message", handleMessage2);
|
|
6599
7036
|
return () => window.removeEventListener("message", handleMessage2);
|
|
6600
7037
|
}, [mode, fieldId]);
|
|
6601
|
-
|
|
7038
|
+
useEffect18(() => {
|
|
6602
7039
|
if (mode !== "inline-edit") return;
|
|
6603
7040
|
const handleDropModeMessage = (event) => {
|
|
6604
7041
|
if (event.data?.type === "DROP_MODE_START") {
|
|
@@ -6612,7 +7049,7 @@ function YaContainer({
|
|
|
6612
7049
|
window.addEventListener("message", handleDropModeMessage);
|
|
6613
7050
|
return () => window.removeEventListener("message", handleDropModeMessage);
|
|
6614
7051
|
}, [mode]);
|
|
6615
|
-
const handleDragEnter =
|
|
7052
|
+
const handleDragEnter = useCallback20(
|
|
6616
7053
|
(e) => {
|
|
6617
7054
|
if (!isDropMode) return;
|
|
6618
7055
|
e.preventDefault();
|
|
@@ -6636,7 +7073,7 @@ function YaContainer({
|
|
|
6636
7073
|
},
|
|
6637
7074
|
[isDropMode, fieldId]
|
|
6638
7075
|
);
|
|
6639
|
-
const handleDragOver =
|
|
7076
|
+
const handleDragOver = useCallback20(
|
|
6640
7077
|
(e) => {
|
|
6641
7078
|
if (!isDropMode) return;
|
|
6642
7079
|
e.preventDefault();
|
|
@@ -6644,7 +7081,7 @@ function YaContainer({
|
|
|
6644
7081
|
},
|
|
6645
7082
|
[isDropMode]
|
|
6646
7083
|
);
|
|
6647
|
-
const handleDragLeave =
|
|
7084
|
+
const handleDragLeave = useCallback20(
|
|
6648
7085
|
(e) => {
|
|
6649
7086
|
if (!isDropMode) return;
|
|
6650
7087
|
e.preventDefault();
|
|
@@ -6658,7 +7095,7 @@ function YaContainer({
|
|
|
6658
7095
|
},
|
|
6659
7096
|
[isDropMode]
|
|
6660
7097
|
);
|
|
6661
|
-
const handleDrop =
|
|
7098
|
+
const handleDrop = useCallback20(
|
|
6662
7099
|
(e) => {
|
|
6663
7100
|
if (!isDropMode) return;
|
|
6664
7101
|
e.preventDefault();
|
|
@@ -6676,7 +7113,7 @@ function YaContainer({
|
|
|
6676
7113
|
},
|
|
6677
7114
|
[isDropMode, fieldId]
|
|
6678
7115
|
);
|
|
6679
|
-
|
|
7116
|
+
useEffect18(() => {
|
|
6680
7117
|
if (!isSelected || mode !== "inline-edit") return;
|
|
6681
7118
|
let lastRectKey = "";
|
|
6682
7119
|
let lastTime = 0;
|
|
@@ -6711,7 +7148,7 @@ function YaContainer({
|
|
|
6711
7148
|
return () => cancelAnimationFrame(rafId);
|
|
6712
7149
|
}, [isSelected, fieldId, mode]);
|
|
6713
7150
|
if (mode === "read-only") {
|
|
6714
|
-
return /* @__PURE__ */
|
|
7151
|
+
return /* @__PURE__ */ jsx18(
|
|
6715
7152
|
Tag,
|
|
6716
7153
|
{
|
|
6717
7154
|
className: `ya-container ${className || ""}`,
|
|
@@ -6754,7 +7191,7 @@ function YaContainer({
|
|
|
6754
7191
|
onDrop: handleDrop,
|
|
6755
7192
|
children: [
|
|
6756
7193
|
children,
|
|
6757
|
-
mode === "inline-edit" && (isHovered || isSelected) && !document.body.classList.contains("builder-selector-active") && /* @__PURE__ */
|
|
7194
|
+
mode === "inline-edit" && (isHovered || isSelected) && !document.body.classList.contains("builder-selector-active") && /* @__PURE__ */ jsx18(
|
|
6758
7195
|
Toolbar,
|
|
6759
7196
|
{
|
|
6760
7197
|
containerRef,
|
|
@@ -6771,10 +7208,10 @@ function YaContainer({
|
|
|
6771
7208
|
}
|
|
6772
7209
|
|
|
6773
7210
|
// src/components/StaticText.tsx
|
|
6774
|
-
import { jsx as
|
|
7211
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
6775
7212
|
function MpText({ fieldId, className, as: Component = "span", children }) {
|
|
6776
7213
|
const content = getContent(fieldId) || (typeof children === "string" ? children : "");
|
|
6777
|
-
return /* @__PURE__ */
|
|
7214
|
+
return /* @__PURE__ */ jsx19(
|
|
6778
7215
|
Component,
|
|
6779
7216
|
{
|
|
6780
7217
|
className,
|
|
@@ -6785,7 +7222,7 @@ function MpText({ fieldId, className, as: Component = "span", children }) {
|
|
|
6785
7222
|
}
|
|
6786
7223
|
|
|
6787
7224
|
// src/components/StaticImage.tsx
|
|
6788
|
-
import { jsx as
|
|
7225
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
6789
7226
|
function parseImageValue2(value) {
|
|
6790
7227
|
if (!value) {
|
|
6791
7228
|
return { src: "" };
|
|
@@ -6821,7 +7258,7 @@ function MpImage({
|
|
|
6821
7258
|
const altText = imageData.alt || alt || fallbackAlt || "";
|
|
6822
7259
|
const objectFit = imageData.objectFit || propObjectFit || "cover";
|
|
6823
7260
|
const objectPosition = getObjectPosition3(imageData) || propObjectPosition || "50% 50%";
|
|
6824
|
-
return /* @__PURE__ */
|
|
7261
|
+
return /* @__PURE__ */ jsx20(
|
|
6825
7262
|
"img",
|
|
6826
7263
|
{
|
|
6827
7264
|
src: resolveAssetUrl(src),
|
|
@@ -6839,7 +7276,7 @@ function MpImage({
|
|
|
6839
7276
|
|
|
6840
7277
|
// src/components/MarkdownText.tsx
|
|
6841
7278
|
import { Fragment as Fragment6 } from "react";
|
|
6842
|
-
import { jsx as
|
|
7279
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
6843
7280
|
function tokenize(text2) {
|
|
6844
7281
|
const tokens = [];
|
|
6845
7282
|
let remaining = text2;
|
|
@@ -6901,13 +7338,13 @@ function tokensToElements(tokens) {
|
|
|
6901
7338
|
return tokens.map((token, index) => {
|
|
6902
7339
|
switch (token.type) {
|
|
6903
7340
|
case "text":
|
|
6904
|
-
return /* @__PURE__ */
|
|
7341
|
+
return /* @__PURE__ */ jsx21(Fragment6, { children: token.content }, index);
|
|
6905
7342
|
case "bold":
|
|
6906
|
-
return /* @__PURE__ */
|
|
7343
|
+
return /* @__PURE__ */ jsx21("strong", { children: token.content }, index);
|
|
6907
7344
|
case "italic":
|
|
6908
|
-
return /* @__PURE__ */
|
|
7345
|
+
return /* @__PURE__ */ jsx21("em", { children: token.content }, index);
|
|
6909
7346
|
case "link":
|
|
6910
|
-
return /* @__PURE__ */
|
|
7347
|
+
return /* @__PURE__ */ jsx21(
|
|
6911
7348
|
"a",
|
|
6912
7349
|
{
|
|
6913
7350
|
href: token.url,
|
|
@@ -6919,7 +7356,7 @@ function tokensToElements(tokens) {
|
|
|
6919
7356
|
index
|
|
6920
7357
|
);
|
|
6921
7358
|
case "newline":
|
|
6922
|
-
return /* @__PURE__ */
|
|
7359
|
+
return /* @__PURE__ */ jsx21("br", {}, index);
|
|
6923
7360
|
default:
|
|
6924
7361
|
return null;
|
|
6925
7362
|
}
|
|
@@ -6931,15 +7368,480 @@ function parseMarkdownToElements(content) {
|
|
|
6931
7368
|
}
|
|
6932
7369
|
function MarkdownText({ content, className }) {
|
|
6933
7370
|
const elements = parseMarkdownToElements(content);
|
|
6934
|
-
return /* @__PURE__ */
|
|
7371
|
+
return /* @__PURE__ */ jsx21("span", { className, children: elements });
|
|
7372
|
+
}
|
|
7373
|
+
|
|
7374
|
+
// src/components/CollectionPage.tsx
|
|
7375
|
+
import { useParams } from "wouter";
|
|
7376
|
+
|
|
7377
|
+
// src/hooks/useCollection.ts
|
|
7378
|
+
import { useState as useState19, useEffect as useEffect19, useCallback as useCallback21, useRef as useRef20 } from "react";
|
|
7379
|
+
|
|
7380
|
+
// src/lib/collection-client.ts
|
|
7381
|
+
function getEnvVar(key) {
|
|
7382
|
+
try {
|
|
7383
|
+
const env = import.meta.env;
|
|
7384
|
+
return env?.[key];
|
|
7385
|
+
} catch {
|
|
7386
|
+
return void 0;
|
|
7387
|
+
}
|
|
7388
|
+
}
|
|
7389
|
+
function getConfig() {
|
|
7390
|
+
if (typeof window === "undefined") {
|
|
7391
|
+
throw new Error("CollectionClient requires browser environment");
|
|
7392
|
+
}
|
|
7393
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
7394
|
+
const appId = runtimeConfig?.appId || runtimeConfig?.siteId || getEnvVar("YA_APP_ID") || getEnvVar("YA_SITE_ID");
|
|
7395
|
+
const apiUrl = runtimeConfig?.apiUrl || getEnvVar("YA_API_URL");
|
|
7396
|
+
if (!appId) {
|
|
7397
|
+
throw new Error("App ID not configured (check YOAMIGO_CONFIG.appId or YA_APP_ID)");
|
|
7398
|
+
}
|
|
7399
|
+
if (!apiUrl) {
|
|
7400
|
+
throw new Error("API URL not configured (check YOAMIGO_CONFIG.apiUrl or YA_API_URL)");
|
|
7401
|
+
}
|
|
7402
|
+
return { apiUrl, appId };
|
|
7403
|
+
}
|
|
7404
|
+
var CollectionClient = class {
|
|
7405
|
+
apiUrl;
|
|
7406
|
+
appId;
|
|
7407
|
+
constructor(config) {
|
|
7408
|
+
this.apiUrl = config.apiUrl;
|
|
7409
|
+
this.appId = config.appId;
|
|
7410
|
+
}
|
|
7411
|
+
/**
|
|
7412
|
+
* List records from a collection with optional filtering, pagination, and sorting.
|
|
7413
|
+
*/
|
|
7414
|
+
async list(collection, options = {}) {
|
|
7415
|
+
const { filters, limit, offset, orderBy, orderDir } = options;
|
|
7416
|
+
const url = new URL(`${this.apiUrl}/api/apps/${this.appId}/collections/${collection}/records`);
|
|
7417
|
+
if (filters && filters.length > 0) {
|
|
7418
|
+
url.searchParams.set("filters", JSON.stringify(filters));
|
|
7419
|
+
}
|
|
7420
|
+
if (limit !== void 0) {
|
|
7421
|
+
url.searchParams.set("limit", String(limit));
|
|
7422
|
+
}
|
|
7423
|
+
if (offset !== void 0) {
|
|
7424
|
+
url.searchParams.set("offset", String(offset));
|
|
7425
|
+
}
|
|
7426
|
+
if (orderBy) {
|
|
7427
|
+
url.searchParams.set("orderBy", orderBy);
|
|
7428
|
+
}
|
|
7429
|
+
if (orderDir) {
|
|
7430
|
+
url.searchParams.set("orderDir", orderDir);
|
|
7431
|
+
}
|
|
7432
|
+
try {
|
|
7433
|
+
const response = await fetch(url.toString(), {
|
|
7434
|
+
method: "GET",
|
|
7435
|
+
headers: {
|
|
7436
|
+
"Content-Type": "application/json"
|
|
7437
|
+
}
|
|
7438
|
+
});
|
|
7439
|
+
const result = await response.json();
|
|
7440
|
+
if (!response.ok) {
|
|
7441
|
+
return {
|
|
7442
|
+
success: false,
|
|
7443
|
+
error: result.error || `HTTP ${response.status}`,
|
|
7444
|
+
errorCode: result.errorCode
|
|
7445
|
+
};
|
|
7446
|
+
}
|
|
7447
|
+
return {
|
|
7448
|
+
success: true,
|
|
7449
|
+
data: result.data,
|
|
7450
|
+
totalCount: result.totalCount
|
|
7451
|
+
};
|
|
7452
|
+
} catch (error) {
|
|
7453
|
+
return {
|
|
7454
|
+
success: false,
|
|
7455
|
+
error: error instanceof Error ? error.message : "Network error"
|
|
7456
|
+
};
|
|
7457
|
+
}
|
|
7458
|
+
}
|
|
7459
|
+
/**
|
|
7460
|
+
* Get a single record by its ID.
|
|
7461
|
+
*/
|
|
7462
|
+
async getById(collection, id) {
|
|
7463
|
+
const url = `${this.apiUrl}/api/apps/${this.appId}/collections/${collection}/records/${id}`;
|
|
7464
|
+
try {
|
|
7465
|
+
const response = await fetch(url, {
|
|
7466
|
+
method: "GET",
|
|
7467
|
+
headers: {
|
|
7468
|
+
"Content-Type": "application/json"
|
|
7469
|
+
}
|
|
7470
|
+
});
|
|
7471
|
+
const result = await response.json();
|
|
7472
|
+
if (!response.ok) {
|
|
7473
|
+
return {
|
|
7474
|
+
success: false,
|
|
7475
|
+
error: result.error || `HTTP ${response.status}`,
|
|
7476
|
+
errorCode: result.errorCode
|
|
7477
|
+
};
|
|
7478
|
+
}
|
|
7479
|
+
return {
|
|
7480
|
+
success: true,
|
|
7481
|
+
data: result.data
|
|
7482
|
+
};
|
|
7483
|
+
} catch (error) {
|
|
7484
|
+
return {
|
|
7485
|
+
success: false,
|
|
7486
|
+
error: error instanceof Error ? error.message : "Network error"
|
|
7487
|
+
};
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7490
|
+
/**
|
|
7491
|
+
* Get a single record by a slug field value.
|
|
7492
|
+
* Convenience method that uses list with a filter.
|
|
7493
|
+
*/
|
|
7494
|
+
async getBySlug(collection, slugField, slugValue) {
|
|
7495
|
+
const result = await this.list(collection, {
|
|
7496
|
+
filters: [
|
|
7497
|
+
{ field: slugField, operator: "eq", value: slugValue }
|
|
7498
|
+
],
|
|
7499
|
+
limit: 1
|
|
7500
|
+
});
|
|
7501
|
+
if (!result.success) {
|
|
7502
|
+
return {
|
|
7503
|
+
success: false,
|
|
7504
|
+
error: result.error,
|
|
7505
|
+
errorCode: result.errorCode
|
|
7506
|
+
};
|
|
7507
|
+
}
|
|
7508
|
+
if (!result.data || result.data.length === 0) {
|
|
7509
|
+
return {
|
|
7510
|
+
success: false,
|
|
7511
|
+
error: "Record not found",
|
|
7512
|
+
errorCode: "NOT_FOUND"
|
|
7513
|
+
};
|
|
7514
|
+
}
|
|
7515
|
+
return {
|
|
7516
|
+
success: true,
|
|
7517
|
+
data: result.data[0]
|
|
7518
|
+
};
|
|
7519
|
+
}
|
|
7520
|
+
};
|
|
7521
|
+
var cachedClient = null;
|
|
7522
|
+
function getCollectionClient() {
|
|
7523
|
+
if (!cachedClient) {
|
|
7524
|
+
const config = getConfig();
|
|
7525
|
+
cachedClient = new CollectionClient(config);
|
|
7526
|
+
}
|
|
7527
|
+
return cachedClient;
|
|
7528
|
+
}
|
|
7529
|
+
function resetCollectionClient() {
|
|
7530
|
+
cachedClient = null;
|
|
7531
|
+
}
|
|
7532
|
+
|
|
7533
|
+
// src/hooks/useCollection.ts
|
|
7534
|
+
function useCollectionList(options) {
|
|
7535
|
+
const {
|
|
7536
|
+
collection,
|
|
7537
|
+
filters,
|
|
7538
|
+
page = 1,
|
|
7539
|
+
pageSize = 10,
|
|
7540
|
+
orderBy,
|
|
7541
|
+
orderDir,
|
|
7542
|
+
enabled = true
|
|
7543
|
+
} = options;
|
|
7544
|
+
const [data, setData] = useState19(null);
|
|
7545
|
+
const [isLoading, setIsLoading] = useState19(true);
|
|
7546
|
+
const [error, setError] = useState19(null);
|
|
7547
|
+
const [totalCount, setTotalCount] = useState19(0);
|
|
7548
|
+
const [currentPage, setCurrentPage] = useState19(page);
|
|
7549
|
+
const fetchVersion = useRef20(0);
|
|
7550
|
+
const fetchData = useCallback21(async (pageNum) => {
|
|
7551
|
+
if (!enabled) {
|
|
7552
|
+
setIsLoading(false);
|
|
7553
|
+
return;
|
|
7554
|
+
}
|
|
7555
|
+
setIsLoading(true);
|
|
7556
|
+
setError(null);
|
|
7557
|
+
const version = ++fetchVersion.current;
|
|
7558
|
+
try {
|
|
7559
|
+
const client = getCollectionClient();
|
|
7560
|
+
const offset = (pageNum - 1) * pageSize;
|
|
7561
|
+
const result = await client.list(collection, {
|
|
7562
|
+
filters,
|
|
7563
|
+
limit: pageSize,
|
|
7564
|
+
offset,
|
|
7565
|
+
orderBy,
|
|
7566
|
+
orderDir
|
|
7567
|
+
});
|
|
7568
|
+
if (version !== fetchVersion.current) return;
|
|
7569
|
+
if (!result.success) {
|
|
7570
|
+
setError(result.error || "Failed to fetch collection");
|
|
7571
|
+
setData(null);
|
|
7572
|
+
setTotalCount(0);
|
|
7573
|
+
} else {
|
|
7574
|
+
setData(result.data || []);
|
|
7575
|
+
setTotalCount(result.totalCount || 0);
|
|
7576
|
+
setCurrentPage(pageNum);
|
|
7577
|
+
}
|
|
7578
|
+
} catch (err) {
|
|
7579
|
+
if (version !== fetchVersion.current) return;
|
|
7580
|
+
setError(err instanceof Error ? err.message : "Unknown error");
|
|
7581
|
+
setData(null);
|
|
7582
|
+
} finally {
|
|
7583
|
+
if (version === fetchVersion.current) {
|
|
7584
|
+
setIsLoading(false);
|
|
7585
|
+
}
|
|
7586
|
+
}
|
|
7587
|
+
}, [collection, JSON.stringify(filters), pageSize, orderBy, orderDir, enabled]);
|
|
7588
|
+
useEffect19(() => {
|
|
7589
|
+
fetchData(page);
|
|
7590
|
+
}, [fetchData, page]);
|
|
7591
|
+
const totalPages = Math.ceil(totalCount / pageSize);
|
|
7592
|
+
const hasMore = currentPage < totalPages;
|
|
7593
|
+
const refetch = useCallback21(() => {
|
|
7594
|
+
fetchData(currentPage);
|
|
7595
|
+
}, [fetchData, currentPage]);
|
|
7596
|
+
const goToPage = useCallback21((newPage) => {
|
|
7597
|
+
if (newPage >= 1 && newPage <= Math.max(1, totalPages)) {
|
|
7598
|
+
fetchData(newPage);
|
|
7599
|
+
}
|
|
7600
|
+
}, [fetchData, totalPages]);
|
|
7601
|
+
return {
|
|
7602
|
+
data,
|
|
7603
|
+
isLoading,
|
|
7604
|
+
error,
|
|
7605
|
+
totalCount,
|
|
7606
|
+
hasMore,
|
|
7607
|
+
currentPage,
|
|
7608
|
+
totalPages,
|
|
7609
|
+
refetch,
|
|
7610
|
+
goToPage
|
|
7611
|
+
};
|
|
7612
|
+
}
|
|
7613
|
+
function useCollectionRecord(options) {
|
|
7614
|
+
const {
|
|
7615
|
+
collection,
|
|
7616
|
+
slugField,
|
|
7617
|
+
slugValue,
|
|
7618
|
+
enabled = true
|
|
7619
|
+
} = options;
|
|
7620
|
+
const [data, setData] = useState19(null);
|
|
7621
|
+
const [isLoading, setIsLoading] = useState19(true);
|
|
7622
|
+
const [error, setError] = useState19(null);
|
|
7623
|
+
const [notFound, setNotFound] = useState19(false);
|
|
7624
|
+
const fetchVersion = useRef20(0);
|
|
7625
|
+
const fetchData = useCallback21(async () => {
|
|
7626
|
+
if (!enabled || !slugValue) {
|
|
7627
|
+
setIsLoading(false);
|
|
7628
|
+
return;
|
|
7629
|
+
}
|
|
7630
|
+
setIsLoading(true);
|
|
7631
|
+
setError(null);
|
|
7632
|
+
setNotFound(false);
|
|
7633
|
+
const version = ++fetchVersion.current;
|
|
7634
|
+
try {
|
|
7635
|
+
const client = getCollectionClient();
|
|
7636
|
+
const result = await client.getBySlug(
|
|
7637
|
+
collection,
|
|
7638
|
+
slugField,
|
|
7639
|
+
slugValue
|
|
7640
|
+
);
|
|
7641
|
+
if (version !== fetchVersion.current) return;
|
|
7642
|
+
if (!result.success) {
|
|
7643
|
+
if (result.errorCode === "NOT_FOUND") {
|
|
7644
|
+
setNotFound(true);
|
|
7645
|
+
setData(null);
|
|
7646
|
+
} else {
|
|
7647
|
+
setError(result.error || "Failed to fetch record");
|
|
7648
|
+
setData(null);
|
|
7649
|
+
}
|
|
7650
|
+
} else {
|
|
7651
|
+
setData(result.data || null);
|
|
7652
|
+
}
|
|
7653
|
+
} catch (err) {
|
|
7654
|
+
if (version !== fetchVersion.current) return;
|
|
7655
|
+
setError(err instanceof Error ? err.message : "Unknown error");
|
|
7656
|
+
setData(null);
|
|
7657
|
+
} finally {
|
|
7658
|
+
if (version === fetchVersion.current) {
|
|
7659
|
+
setIsLoading(false);
|
|
7660
|
+
}
|
|
7661
|
+
}
|
|
7662
|
+
}, [collection, slugField, slugValue, enabled]);
|
|
7663
|
+
useEffect19(() => {
|
|
7664
|
+
fetchData();
|
|
7665
|
+
}, [fetchData]);
|
|
7666
|
+
const refetch = useCallback21(() => {
|
|
7667
|
+
fetchData();
|
|
7668
|
+
}, [fetchData]);
|
|
7669
|
+
return {
|
|
7670
|
+
data,
|
|
7671
|
+
isLoading,
|
|
7672
|
+
error,
|
|
7673
|
+
notFound,
|
|
7674
|
+
refetch
|
|
7675
|
+
};
|
|
7676
|
+
}
|
|
7677
|
+
|
|
7678
|
+
// src/components/CollectionPage.tsx
|
|
7679
|
+
function CollectionListPage({
|
|
7680
|
+
collection,
|
|
7681
|
+
pageSize = 10,
|
|
7682
|
+
page = 1,
|
|
7683
|
+
filters,
|
|
7684
|
+
orderBy,
|
|
7685
|
+
orderDir,
|
|
7686
|
+
enabled = true,
|
|
7687
|
+
render
|
|
7688
|
+
}) {
|
|
7689
|
+
const result = useCollectionList({
|
|
7690
|
+
collection,
|
|
7691
|
+
pageSize,
|
|
7692
|
+
page,
|
|
7693
|
+
filters,
|
|
7694
|
+
orderBy,
|
|
7695
|
+
orderDir,
|
|
7696
|
+
enabled
|
|
7697
|
+
});
|
|
7698
|
+
return render(result);
|
|
7699
|
+
}
|
|
7700
|
+
function CollectionDetailPage({
|
|
7701
|
+
collection,
|
|
7702
|
+
slugField = "slug",
|
|
7703
|
+
slugParam = "slug",
|
|
7704
|
+
slug: slugOverride,
|
|
7705
|
+
enabled = true,
|
|
7706
|
+
render
|
|
7707
|
+
}) {
|
|
7708
|
+
const params = useParams();
|
|
7709
|
+
const slug = slugOverride || params[slugParam] || "";
|
|
7710
|
+
const result = useCollectionRecord({
|
|
7711
|
+
collection,
|
|
7712
|
+
slugField,
|
|
7713
|
+
slugValue: slug,
|
|
7714
|
+
enabled: enabled && Boolean(slug)
|
|
7715
|
+
});
|
|
7716
|
+
return render({
|
|
7717
|
+
...result,
|
|
7718
|
+
slug
|
|
7719
|
+
});
|
|
7720
|
+
}
|
|
7721
|
+
|
|
7722
|
+
// src/components/CollectionList.tsx
|
|
7723
|
+
import { Fragment as Fragment7, jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
7724
|
+
function CollectionList({
|
|
7725
|
+
collection,
|
|
7726
|
+
prefix,
|
|
7727
|
+
filters,
|
|
7728
|
+
page,
|
|
7729
|
+
pageSize,
|
|
7730
|
+
orderBy,
|
|
7731
|
+
orderDir,
|
|
7732
|
+
children,
|
|
7733
|
+
skeleton,
|
|
7734
|
+
empty,
|
|
7735
|
+
error,
|
|
7736
|
+
appId,
|
|
7737
|
+
enabled = true,
|
|
7738
|
+
pagination,
|
|
7739
|
+
wrapper: Wrapper
|
|
7740
|
+
}) {
|
|
7741
|
+
const result = useCollectionList({
|
|
7742
|
+
collection,
|
|
7743
|
+
filters,
|
|
7744
|
+
page,
|
|
7745
|
+
pageSize,
|
|
7746
|
+
orderBy,
|
|
7747
|
+
orderDir,
|
|
7748
|
+
enabled
|
|
7749
|
+
});
|
|
7750
|
+
if (result.isLoading) {
|
|
7751
|
+
const skeletonCount = pageSize || 10;
|
|
7752
|
+
const skeletonItems = Array.from(
|
|
7753
|
+
{ length: skeletonCount },
|
|
7754
|
+
(_, index) => skeleton({ index })
|
|
7755
|
+
);
|
|
7756
|
+
return Wrapper ? /* @__PURE__ */ jsx22(Wrapper, { children: skeletonItems }) : /* @__PURE__ */ jsx22(Fragment7, { children: skeletonItems });
|
|
7757
|
+
}
|
|
7758
|
+
if (result.error) {
|
|
7759
|
+
return error?.(result.error) ?? null;
|
|
7760
|
+
}
|
|
7761
|
+
if (!result.data || result.data.length === 0) {
|
|
7762
|
+
return empty ?? null;
|
|
7763
|
+
}
|
|
7764
|
+
const items = result.data.map((record, index) => {
|
|
7765
|
+
const fieldPrefix = `${prefix}.${record.id}`;
|
|
7766
|
+
return /* @__PURE__ */ jsx22(
|
|
7767
|
+
CollectionContentProvider,
|
|
7768
|
+
{
|
|
7769
|
+
record: record.data,
|
|
7770
|
+
prefix: fieldPrefix,
|
|
7771
|
+
recordId: record.id,
|
|
7772
|
+
collectionSlug: collection,
|
|
7773
|
+
appId,
|
|
7774
|
+
children: children({ record, index, fieldPrefix })
|
|
7775
|
+
},
|
|
7776
|
+
record.id
|
|
7777
|
+
);
|
|
7778
|
+
});
|
|
7779
|
+
const content = Wrapper ? /* @__PURE__ */ jsx22(Wrapper, { children: items }) : /* @__PURE__ */ jsx22(Fragment7, { children: items });
|
|
7780
|
+
if (pagination) {
|
|
7781
|
+
return /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
7782
|
+
content,
|
|
7783
|
+
pagination({
|
|
7784
|
+
currentPage: result.currentPage,
|
|
7785
|
+
totalPages: result.totalPages,
|
|
7786
|
+
totalCount: result.totalCount,
|
|
7787
|
+
hasMore: result.hasMore,
|
|
7788
|
+
goToPage: result.goToPage
|
|
7789
|
+
})
|
|
7790
|
+
] });
|
|
7791
|
+
}
|
|
7792
|
+
return content;
|
|
7793
|
+
}
|
|
7794
|
+
|
|
7795
|
+
// src/components/CollectionItem.tsx
|
|
7796
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
7797
|
+
function CollectionItem({
|
|
7798
|
+
collection,
|
|
7799
|
+
prefix,
|
|
7800
|
+
slugField = "slug",
|
|
7801
|
+
slugValue,
|
|
7802
|
+
children,
|
|
7803
|
+
skeleton,
|
|
7804
|
+
appId,
|
|
7805
|
+
enabled = true
|
|
7806
|
+
}) {
|
|
7807
|
+
const result = useCollectionRecord({
|
|
7808
|
+
collection,
|
|
7809
|
+
slugField,
|
|
7810
|
+
slugValue,
|
|
7811
|
+
enabled: enabled && Boolean(slugValue)
|
|
7812
|
+
});
|
|
7813
|
+
const renderProps = {
|
|
7814
|
+
record: result.data,
|
|
7815
|
+
isLoading: result.isLoading,
|
|
7816
|
+
notFound: result.notFound,
|
|
7817
|
+
error: result.error,
|
|
7818
|
+
refetch: result.refetch
|
|
7819
|
+
};
|
|
7820
|
+
if (result.isLoading) {
|
|
7821
|
+
return skeleton;
|
|
7822
|
+
}
|
|
7823
|
+
if (result.error || result.notFound || !result.data) {
|
|
7824
|
+
return children(renderProps);
|
|
7825
|
+
}
|
|
7826
|
+
return /* @__PURE__ */ jsx23(
|
|
7827
|
+
CollectionContentProvider,
|
|
7828
|
+
{
|
|
7829
|
+
record: result.data.data,
|
|
7830
|
+
prefix,
|
|
7831
|
+
recordId: result.data.id,
|
|
7832
|
+
collectionSlug: collection,
|
|
7833
|
+
appId,
|
|
7834
|
+
children: children(renderProps)
|
|
7835
|
+
}
|
|
7836
|
+
);
|
|
6935
7837
|
}
|
|
6936
7838
|
|
|
6937
7839
|
// src/router/Link.tsx
|
|
6938
7840
|
import { Link as WouterLink2 } from "wouter";
|
|
6939
|
-
import { jsx as
|
|
7841
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
6940
7842
|
function Link2({ to, href, children, className, onClick, replace, ...props }) {
|
|
6941
7843
|
const target = href ?? to ?? "/";
|
|
6942
|
-
return /* @__PURE__ */
|
|
7844
|
+
return /* @__PURE__ */ jsx24(WouterLink2, { href: target, className, onClick, replace, ...props, children });
|
|
6943
7845
|
}
|
|
6944
7846
|
|
|
6945
7847
|
// src/router/useNavigate.ts
|
|
@@ -6958,7 +7860,7 @@ function useNavigate() {
|
|
|
6958
7860
|
|
|
6959
7861
|
// src/router/Router.tsx
|
|
6960
7862
|
import { Router as WouterRouter } from "wouter";
|
|
6961
|
-
import { jsx as
|
|
7863
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
6962
7864
|
function detectBasename() {
|
|
6963
7865
|
if (typeof window === "undefined") return "";
|
|
6964
7866
|
const sessionMatch = window.location.pathname.match(/^\/session\/[^/]+/);
|
|
@@ -6973,11 +7875,11 @@ function detectBasename() {
|
|
|
6973
7875
|
}
|
|
6974
7876
|
function Router({ children, base }) {
|
|
6975
7877
|
const basename = base ?? detectBasename();
|
|
6976
|
-
return /* @__PURE__ */
|
|
7878
|
+
return /* @__PURE__ */ jsx25(WouterRouter, { base: basename, children });
|
|
6977
7879
|
}
|
|
6978
7880
|
|
|
6979
7881
|
// src/router/ScrollRestoration.tsx
|
|
6980
|
-
import { useEffect as
|
|
7882
|
+
import { useEffect as useEffect20, useRef as useRef21 } from "react";
|
|
6981
7883
|
import { useLocation as useLocation3 } from "wouter";
|
|
6982
7884
|
var SCROLL_POSITIONS_KEY = "yoamigo-scroll-positions";
|
|
6983
7885
|
var HISTORY_INDEX_KEY = "yoamigo-history-index";
|
|
@@ -7013,10 +7915,10 @@ function setHistoryIndex(index) {
|
|
|
7013
7915
|
var globalHistoryIndex = 0;
|
|
7014
7916
|
function ScrollRestoration() {
|
|
7015
7917
|
const [location] = useLocation3();
|
|
7016
|
-
const previousLocation =
|
|
7017
|
-
const isPopState =
|
|
7018
|
-
const scrollPositionsRef =
|
|
7019
|
-
|
|
7918
|
+
const previousLocation = useRef21(location);
|
|
7919
|
+
const isPopState = useRef21(false);
|
|
7920
|
+
const scrollPositionsRef = useRef21({});
|
|
7921
|
+
useEffect20(() => {
|
|
7020
7922
|
if (typeof history !== "undefined" && "scrollRestoration" in history) {
|
|
7021
7923
|
history.scrollRestoration = "manual";
|
|
7022
7924
|
}
|
|
@@ -7034,7 +7936,7 @@ function ScrollRestoration() {
|
|
|
7034
7936
|
window.addEventListener("popstate", handlePopState);
|
|
7035
7937
|
return () => window.removeEventListener("popstate", handlePopState);
|
|
7036
7938
|
}, []);
|
|
7037
|
-
|
|
7939
|
+
useEffect20(() => {
|
|
7038
7940
|
if (previousLocation.current === location) return;
|
|
7039
7941
|
const prevHistoryIndex = globalHistoryIndex;
|
|
7040
7942
|
const currentScrollY = window.scrollY;
|
|
@@ -7054,7 +7956,7 @@ function ScrollRestoration() {
|
|
|
7054
7956
|
}
|
|
7055
7957
|
previousLocation.current = location;
|
|
7056
7958
|
}, [location]);
|
|
7057
|
-
|
|
7959
|
+
useEffect20(() => {
|
|
7058
7960
|
let timeoutId;
|
|
7059
7961
|
const handleScroll = () => {
|
|
7060
7962
|
clearTimeout(timeoutId);
|
|
@@ -7073,7 +7975,74 @@ function ScrollRestoration() {
|
|
|
7073
7975
|
}
|
|
7074
7976
|
|
|
7075
7977
|
// src/router/index.ts
|
|
7076
|
-
import { Route, Switch, useParams } from "wouter";
|
|
7978
|
+
import { Route, Switch, useParams as useParams2, useLocation as useLocation4 } from "wouter";
|
|
7979
|
+
|
|
7980
|
+
// src/router/route-utils.ts
|
|
7981
|
+
function filePathToRoutePath(filePath, options = {}) {
|
|
7982
|
+
const {
|
|
7983
|
+
pagesDir = "/src/pages",
|
|
7984
|
+
extensions = [".tsx", ".ts", ".jsx", ".js"]
|
|
7985
|
+
} = options;
|
|
7986
|
+
let path = filePath;
|
|
7987
|
+
if (path.startsWith(pagesDir)) {
|
|
7988
|
+
path = path.slice(pagesDir.length);
|
|
7989
|
+
}
|
|
7990
|
+
for (const ext of extensions) {
|
|
7991
|
+
if (path.endsWith(ext)) {
|
|
7992
|
+
path = path.slice(0, -ext.length);
|
|
7993
|
+
break;
|
|
7994
|
+
}
|
|
7995
|
+
}
|
|
7996
|
+
if (path.endsWith("/index")) {
|
|
7997
|
+
path = path.slice(0, -6) || "/";
|
|
7998
|
+
}
|
|
7999
|
+
path = path.replace(/\[\.\.\.([^\]]+)\]/g, ":$1*");
|
|
8000
|
+
path = path.replace(/\[([^\]]+)\]/g, ":$1");
|
|
8001
|
+
if (!path.startsWith("/")) {
|
|
8002
|
+
path = "/" + path;
|
|
8003
|
+
}
|
|
8004
|
+
if (path === "" || path === "/index") {
|
|
8005
|
+
return "/";
|
|
8006
|
+
}
|
|
8007
|
+
return path;
|
|
8008
|
+
}
|
|
8009
|
+
function extractRouteParams(filePath) {
|
|
8010
|
+
const matches = filePath.matchAll(/\[\.\.\.([^\]]+)\]|\[([^\]]+)\]/g);
|
|
8011
|
+
const params = [];
|
|
8012
|
+
for (const match of matches) {
|
|
8013
|
+
params.push(match[1] || match[2]);
|
|
8014
|
+
}
|
|
8015
|
+
return params;
|
|
8016
|
+
}
|
|
8017
|
+
function createRouteDefinition(filePath, options) {
|
|
8018
|
+
const path = filePathToRoutePath(filePath, options);
|
|
8019
|
+
const params = extractRouteParams(filePath);
|
|
8020
|
+
return {
|
|
8021
|
+
path,
|
|
8022
|
+
filePath,
|
|
8023
|
+
params,
|
|
8024
|
+
isDynamic: params.length > 0
|
|
8025
|
+
};
|
|
8026
|
+
}
|
|
8027
|
+
function sortRoutesBySpecificity(routes) {
|
|
8028
|
+
return [...routes].sort((a, b) => {
|
|
8029
|
+
if (!a.isDynamic && b.isDynamic) return -1;
|
|
8030
|
+
if (a.isDynamic && !b.isDynamic) return 1;
|
|
8031
|
+
const aSegments = a.path.split("/").filter(Boolean);
|
|
8032
|
+
const bSegments = b.path.split("/").filter(Boolean);
|
|
8033
|
+
if (aSegments.length !== bSegments.length) {
|
|
8034
|
+
return bSegments.length - aSegments.length;
|
|
8035
|
+
}
|
|
8036
|
+
return a.params.length - b.params.length;
|
|
8037
|
+
});
|
|
8038
|
+
}
|
|
8039
|
+
function generatePath(routePath, params) {
|
|
8040
|
+
let path = routePath;
|
|
8041
|
+
for (const [key, value] of Object.entries(params)) {
|
|
8042
|
+
path = path.replace(new RegExp(`:${key}\\*?`, "g"), encodeURIComponent(value));
|
|
8043
|
+
}
|
|
8044
|
+
return path;
|
|
8045
|
+
}
|
|
7077
8046
|
|
|
7078
8047
|
// src/lib/content-helpers.ts
|
|
7079
8048
|
function text(content) {
|
|
@@ -7091,8 +8060,952 @@ function video(config) {
|
|
|
7091
8060
|
function embed(config) {
|
|
7092
8061
|
return JSON.stringify(config);
|
|
7093
8062
|
}
|
|
8063
|
+
|
|
8064
|
+
// src/contexts/CartContext.tsx
|
|
8065
|
+
import { createContext as createContext5, useContext as useContext5, useEffect as useEffect22 } from "react";
|
|
8066
|
+
|
|
8067
|
+
// src/hooks/useCart.ts
|
|
8068
|
+
import { useState as useState20, useEffect as useEffect21, useCallback as useCallback22, useRef as useRef22 } from "react";
|
|
8069
|
+
|
|
8070
|
+
// src/lib/cart-storage.ts
|
|
8071
|
+
var CART_STORAGE_KEY_PREFIX = "yoamigo_cart_";
|
|
8072
|
+
var SESSION_ID_KEY = "yoamigo_cart_session";
|
|
8073
|
+
var CART_EXPIRY_DAYS = 7;
|
|
8074
|
+
function generateSessionId() {
|
|
8075
|
+
return `session_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
|
|
8076
|
+
}
|
|
8077
|
+
function getSessionId() {
|
|
8078
|
+
if (typeof window === "undefined") {
|
|
8079
|
+
throw new Error("getSessionId requires browser environment");
|
|
8080
|
+
}
|
|
8081
|
+
let sessionId = localStorage.getItem(SESSION_ID_KEY);
|
|
8082
|
+
if (!sessionId) {
|
|
8083
|
+
sessionId = generateSessionId();
|
|
8084
|
+
localStorage.setItem(SESSION_ID_KEY, sessionId);
|
|
8085
|
+
}
|
|
8086
|
+
return sessionId;
|
|
8087
|
+
}
|
|
8088
|
+
function clearSessionId() {
|
|
8089
|
+
if (typeof window === "undefined") return;
|
|
8090
|
+
localStorage.removeItem(SESSION_ID_KEY);
|
|
8091
|
+
}
|
|
8092
|
+
function getStorageKey(appId) {
|
|
8093
|
+
return `${CART_STORAGE_KEY_PREFIX}${appId}`;
|
|
8094
|
+
}
|
|
8095
|
+
function getLocalCart(appId) {
|
|
8096
|
+
if (typeof window === "undefined") return null;
|
|
8097
|
+
try {
|
|
8098
|
+
const key = getStorageKey(appId);
|
|
8099
|
+
const data = localStorage.getItem(key);
|
|
8100
|
+
if (!data) return null;
|
|
8101
|
+
const cart = JSON.parse(data);
|
|
8102
|
+
const updatedAt = new Date(cart.updatedAt);
|
|
8103
|
+
const expiryDate = /* @__PURE__ */ new Date();
|
|
8104
|
+
expiryDate.setDate(expiryDate.getDate() - CART_EXPIRY_DAYS);
|
|
8105
|
+
if (updatedAt < expiryDate) {
|
|
8106
|
+
localStorage.removeItem(key);
|
|
8107
|
+
return null;
|
|
8108
|
+
}
|
|
8109
|
+
return cart;
|
|
8110
|
+
} catch (error) {
|
|
8111
|
+
console.warn("Failed to read cart from localStorage:", error);
|
|
8112
|
+
return null;
|
|
8113
|
+
}
|
|
8114
|
+
}
|
|
8115
|
+
function saveLocalCart(appId, items) {
|
|
8116
|
+
if (typeof window === "undefined") return;
|
|
8117
|
+
try {
|
|
8118
|
+
const key = getStorageKey(appId);
|
|
8119
|
+
const cart = {
|
|
8120
|
+
sessionId: getSessionId(),
|
|
8121
|
+
items,
|
|
8122
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8123
|
+
};
|
|
8124
|
+
localStorage.setItem(key, JSON.stringify(cart));
|
|
8125
|
+
} catch (error) {
|
|
8126
|
+
console.warn("Failed to save cart to localStorage:", error);
|
|
8127
|
+
}
|
|
8128
|
+
}
|
|
8129
|
+
function clearLocalCart(appId) {
|
|
8130
|
+
if (typeof window === "undefined") return;
|
|
8131
|
+
try {
|
|
8132
|
+
const key = getStorageKey(appId);
|
|
8133
|
+
localStorage.removeItem(key);
|
|
8134
|
+
} catch (error) {
|
|
8135
|
+
console.warn("Failed to clear cart from localStorage:", error);
|
|
8136
|
+
}
|
|
8137
|
+
}
|
|
8138
|
+
function getLocalCartItems(appId) {
|
|
8139
|
+
const cart = getLocalCart(appId);
|
|
8140
|
+
return cart?.items || [];
|
|
8141
|
+
}
|
|
8142
|
+
function addLocalCartItem(appId, productId, collectionSlug, quantity = 1, variantId, data) {
|
|
8143
|
+
const items = getLocalCartItems(appId);
|
|
8144
|
+
const existingIndex = items.findIndex(
|
|
8145
|
+
(item) => item.productId === productId && item.variantId === variantId
|
|
8146
|
+
);
|
|
8147
|
+
if (existingIndex >= 0) {
|
|
8148
|
+
items[existingIndex].quantity += quantity;
|
|
8149
|
+
} else {
|
|
8150
|
+
items.push({
|
|
8151
|
+
id: `local_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
8152
|
+
productId,
|
|
8153
|
+
collectionSlug,
|
|
8154
|
+
quantity,
|
|
8155
|
+
variantId,
|
|
8156
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8157
|
+
data
|
|
8158
|
+
});
|
|
8159
|
+
}
|
|
8160
|
+
saveLocalCart(appId, items);
|
|
8161
|
+
return items;
|
|
8162
|
+
}
|
|
8163
|
+
function updateLocalCartItem(appId, itemId, quantity) {
|
|
8164
|
+
const items = getLocalCartItems(appId);
|
|
8165
|
+
const itemIndex = items.findIndex((item) => item.id === itemId);
|
|
8166
|
+
if (itemIndex < 0) return items;
|
|
8167
|
+
if (quantity === 0) {
|
|
8168
|
+
items.splice(itemIndex, 1);
|
|
8169
|
+
} else {
|
|
8170
|
+
items[itemIndex].quantity = quantity;
|
|
8171
|
+
}
|
|
8172
|
+
saveLocalCart(appId, items);
|
|
8173
|
+
return items;
|
|
8174
|
+
}
|
|
8175
|
+
function removeLocalCartItem(appId, itemId) {
|
|
8176
|
+
const items = getLocalCartItems(appId);
|
|
8177
|
+
const filteredItems = items.filter((item) => item.id !== itemId);
|
|
8178
|
+
saveLocalCart(appId, filteredItems);
|
|
8179
|
+
return filteredItems;
|
|
8180
|
+
}
|
|
8181
|
+
function getLocalCartItemCount(appId) {
|
|
8182
|
+
const items = getLocalCartItems(appId);
|
|
8183
|
+
return items.reduce((sum, item) => sum + item.quantity, 0);
|
|
8184
|
+
}
|
|
8185
|
+
|
|
8186
|
+
// src/hooks/useCart.ts
|
|
8187
|
+
function getConfig2(options) {
|
|
8188
|
+
if (typeof window === "undefined") {
|
|
8189
|
+
throw new Error("useCart requires browser environment");
|
|
8190
|
+
}
|
|
8191
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
8192
|
+
const appId = options.appId || runtimeConfig?.appId;
|
|
8193
|
+
const apiUrl = options.apiUrl || runtimeConfig?.apiUrl;
|
|
8194
|
+
if (!appId) {
|
|
8195
|
+
throw new Error("App ID not configured (provide appId option or set YOAMIGO_CONFIG.appId)");
|
|
8196
|
+
}
|
|
8197
|
+
if (!apiUrl) {
|
|
8198
|
+
throw new Error("API URL not configured (provide apiUrl option or set YOAMIGO_CONFIG.apiUrl)");
|
|
8199
|
+
}
|
|
8200
|
+
return { appId, apiUrl };
|
|
8201
|
+
}
|
|
8202
|
+
function useCart(options = {}) {
|
|
8203
|
+
const { userToken } = options;
|
|
8204
|
+
const [items, setItems] = useState20([]);
|
|
8205
|
+
const [isLoading, setIsLoading] = useState20(true);
|
|
8206
|
+
const [error, setError] = useState20(null);
|
|
8207
|
+
const fetchVersion = useRef22(0);
|
|
8208
|
+
const configRef = useRef22(null);
|
|
8209
|
+
if (!configRef.current && typeof window !== "undefined") {
|
|
8210
|
+
try {
|
|
8211
|
+
configRef.current = getConfig2(options);
|
|
8212
|
+
} catch {
|
|
8213
|
+
}
|
|
8214
|
+
}
|
|
8215
|
+
const isAuthenticated = !!userToken;
|
|
8216
|
+
const sessionId = typeof window !== "undefined" ? getSessionId() : null;
|
|
8217
|
+
const apiCall = useCallback22(async (endpoint, method = "GET", body) => {
|
|
8218
|
+
const config = configRef.current;
|
|
8219
|
+
if (!config) throw new Error("Cart not configured");
|
|
8220
|
+
const headers = {
|
|
8221
|
+
"Content-Type": "application/json"
|
|
8222
|
+
};
|
|
8223
|
+
if (isAuthenticated) {
|
|
8224
|
+
headers["X-App-User-Token"] = userToken;
|
|
8225
|
+
} else if (sessionId) {
|
|
8226
|
+
headers["X-Session-Id"] = sessionId;
|
|
8227
|
+
}
|
|
8228
|
+
const response = await fetch(
|
|
8229
|
+
`${config.apiUrl}/api/apps/${config.appId}/cart${endpoint}`,
|
|
8230
|
+
{
|
|
8231
|
+
method,
|
|
8232
|
+
headers,
|
|
8233
|
+
body: body ? JSON.stringify(body) : void 0
|
|
8234
|
+
}
|
|
8235
|
+
);
|
|
8236
|
+
return response.json();
|
|
8237
|
+
}, [userToken, isAuthenticated, sessionId]);
|
|
8238
|
+
const fetchCart = useCallback22(async () => {
|
|
8239
|
+
const config = configRef.current;
|
|
8240
|
+
if (!config) {
|
|
8241
|
+
setIsLoading(false);
|
|
8242
|
+
setError("Cart not configured");
|
|
8243
|
+
return;
|
|
8244
|
+
}
|
|
8245
|
+
const version = ++fetchVersion.current;
|
|
8246
|
+
setIsLoading(true);
|
|
8247
|
+
setError(null);
|
|
8248
|
+
try {
|
|
8249
|
+
if (isAuthenticated || sessionId) {
|
|
8250
|
+
const result = await apiCall("");
|
|
8251
|
+
if (version !== fetchVersion.current) return;
|
|
8252
|
+
if (result.success && result.data) {
|
|
8253
|
+
setItems(result.data.items);
|
|
8254
|
+
} else {
|
|
8255
|
+
if (!isAuthenticated) {
|
|
8256
|
+
setItems(getLocalCartItems(config.appId));
|
|
8257
|
+
} else {
|
|
8258
|
+
setError(result.error || "Failed to fetch cart");
|
|
8259
|
+
}
|
|
8260
|
+
}
|
|
8261
|
+
} else {
|
|
8262
|
+
setItems(getLocalCartItems(config.appId));
|
|
8263
|
+
}
|
|
8264
|
+
} catch (err) {
|
|
8265
|
+
if (version !== fetchVersion.current) return;
|
|
8266
|
+
if (!isAuthenticated) {
|
|
8267
|
+
setItems(getLocalCartItems(config.appId));
|
|
8268
|
+
} else {
|
|
8269
|
+
setError(err instanceof Error ? err.message : "Failed to fetch cart");
|
|
8270
|
+
}
|
|
8271
|
+
} finally {
|
|
8272
|
+
if (version === fetchVersion.current) {
|
|
8273
|
+
setIsLoading(false);
|
|
8274
|
+
}
|
|
8275
|
+
}
|
|
8276
|
+
}, [apiCall, isAuthenticated, sessionId]);
|
|
8277
|
+
useEffect21(() => {
|
|
8278
|
+
fetchCart();
|
|
8279
|
+
}, [fetchCart]);
|
|
8280
|
+
const addItem = useCallback22(async (productId, collectionSlug, quantity = 1, variantId, data) => {
|
|
8281
|
+
const config = configRef.current;
|
|
8282
|
+
if (!config) throw new Error("Cart not configured");
|
|
8283
|
+
setError(null);
|
|
8284
|
+
try {
|
|
8285
|
+
if (isAuthenticated || sessionId) {
|
|
8286
|
+
const result = await apiCall("/items", "POST", {
|
|
8287
|
+
productId,
|
|
8288
|
+
collectionSlug,
|
|
8289
|
+
quantity,
|
|
8290
|
+
variantId,
|
|
8291
|
+
data
|
|
8292
|
+
});
|
|
8293
|
+
if (result.success && result.data) {
|
|
8294
|
+
setItems(result.data.items);
|
|
8295
|
+
} else {
|
|
8296
|
+
throw new Error(result.error || "Failed to add item");
|
|
8297
|
+
}
|
|
8298
|
+
} else {
|
|
8299
|
+
const newItems = addLocalCartItem(config.appId, productId, collectionSlug, quantity, variantId, data);
|
|
8300
|
+
setItems(newItems);
|
|
8301
|
+
}
|
|
8302
|
+
} catch (err) {
|
|
8303
|
+
const message = err instanceof Error ? err.message : "Failed to add item";
|
|
8304
|
+
setError(message);
|
|
8305
|
+
throw err;
|
|
8306
|
+
}
|
|
8307
|
+
}, [apiCall, isAuthenticated, sessionId]);
|
|
8308
|
+
const updateQuantity = useCallback22(async (itemId, quantity) => {
|
|
8309
|
+
const config = configRef.current;
|
|
8310
|
+
if (!config) throw new Error("Cart not configured");
|
|
8311
|
+
setError(null);
|
|
8312
|
+
try {
|
|
8313
|
+
if (isAuthenticated || sessionId) {
|
|
8314
|
+
const result = await apiCall(`/items/${itemId}`, "PATCH", { quantity });
|
|
8315
|
+
if (result.success && result.data) {
|
|
8316
|
+
setItems(result.data.items);
|
|
8317
|
+
} else {
|
|
8318
|
+
throw new Error(result.error || "Failed to update item");
|
|
8319
|
+
}
|
|
8320
|
+
} else {
|
|
8321
|
+
const newItems = updateLocalCartItem(config.appId, itemId, quantity);
|
|
8322
|
+
setItems(newItems);
|
|
8323
|
+
}
|
|
8324
|
+
} catch (err) {
|
|
8325
|
+
const message = err instanceof Error ? err.message : "Failed to update item";
|
|
8326
|
+
setError(message);
|
|
8327
|
+
throw err;
|
|
8328
|
+
}
|
|
8329
|
+
}, [apiCall, isAuthenticated, sessionId]);
|
|
8330
|
+
const removeItem = useCallback22(async (itemId) => {
|
|
8331
|
+
const config = configRef.current;
|
|
8332
|
+
if (!config) throw new Error("Cart not configured");
|
|
8333
|
+
setError(null);
|
|
8334
|
+
try {
|
|
8335
|
+
if (isAuthenticated || sessionId) {
|
|
8336
|
+
const result = await apiCall(`/items/${itemId}`, "DELETE");
|
|
8337
|
+
if (result.success && result.data) {
|
|
8338
|
+
setItems(result.data.items);
|
|
8339
|
+
} else {
|
|
8340
|
+
throw new Error(result.error || "Failed to remove item");
|
|
8341
|
+
}
|
|
8342
|
+
} else {
|
|
8343
|
+
const newItems = removeLocalCartItem(config.appId, itemId);
|
|
8344
|
+
setItems(newItems);
|
|
8345
|
+
}
|
|
8346
|
+
} catch (err) {
|
|
8347
|
+
const message = err instanceof Error ? err.message : "Failed to remove item";
|
|
8348
|
+
setError(message);
|
|
8349
|
+
throw err;
|
|
8350
|
+
}
|
|
8351
|
+
}, [apiCall, isAuthenticated, sessionId]);
|
|
8352
|
+
const clearCart = useCallback22(async () => {
|
|
8353
|
+
const config = configRef.current;
|
|
8354
|
+
if (!config) throw new Error("Cart not configured");
|
|
8355
|
+
setError(null);
|
|
8356
|
+
try {
|
|
8357
|
+
if (isAuthenticated || sessionId) {
|
|
8358
|
+
const result = await apiCall("", "DELETE");
|
|
8359
|
+
if (result.success) {
|
|
8360
|
+
setItems([]);
|
|
8361
|
+
} else {
|
|
8362
|
+
throw new Error(result.error || "Failed to clear cart");
|
|
8363
|
+
}
|
|
8364
|
+
} else {
|
|
8365
|
+
clearLocalCart(config.appId);
|
|
8366
|
+
setItems([]);
|
|
8367
|
+
}
|
|
8368
|
+
} catch (err) {
|
|
8369
|
+
const message = err instanceof Error ? err.message : "Failed to clear cart";
|
|
8370
|
+
setError(message);
|
|
8371
|
+
throw err;
|
|
8372
|
+
}
|
|
8373
|
+
}, [apiCall, isAuthenticated, sessionId]);
|
|
8374
|
+
const mergeGuestCart = useCallback22(async () => {
|
|
8375
|
+
if (!isAuthenticated || !sessionId) return;
|
|
8376
|
+
setError(null);
|
|
8377
|
+
try {
|
|
8378
|
+
const result = await apiCall("/merge", "POST", {
|
|
8379
|
+
guestSessionId: sessionId,
|
|
8380
|
+
mergeStrategy: "sum"
|
|
8381
|
+
});
|
|
8382
|
+
if (result.success && result.data) {
|
|
8383
|
+
setItems(result.data.items);
|
|
8384
|
+
if (configRef.current) {
|
|
8385
|
+
clearLocalCart(configRef.current.appId);
|
|
8386
|
+
}
|
|
8387
|
+
} else {
|
|
8388
|
+
throw new Error(result.error || "Failed to merge cart");
|
|
8389
|
+
}
|
|
8390
|
+
} catch (err) {
|
|
8391
|
+
const message = err instanceof Error ? err.message : "Failed to merge cart";
|
|
8392
|
+
setError(message);
|
|
8393
|
+
}
|
|
8394
|
+
}, [apiCall, isAuthenticated, sessionId]);
|
|
8395
|
+
const itemCount = items.reduce((sum, item) => sum + item.quantity, 0);
|
|
8396
|
+
return {
|
|
8397
|
+
items,
|
|
8398
|
+
itemCount,
|
|
8399
|
+
isLoading,
|
|
8400
|
+
error,
|
|
8401
|
+
addItem,
|
|
8402
|
+
updateQuantity,
|
|
8403
|
+
removeItem,
|
|
8404
|
+
clearCart,
|
|
8405
|
+
mergeGuestCart,
|
|
8406
|
+
refresh: fetchCart
|
|
8407
|
+
};
|
|
8408
|
+
}
|
|
8409
|
+
|
|
8410
|
+
// src/contexts/CartContext.tsx
|
|
8411
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
8412
|
+
var CartContext = createContext5(null);
|
|
8413
|
+
function CartProvider({
|
|
8414
|
+
children,
|
|
8415
|
+
userToken,
|
|
8416
|
+
onUserTokenChange,
|
|
8417
|
+
...options
|
|
8418
|
+
}) {
|
|
8419
|
+
const cart = useCart({ ...options, userToken });
|
|
8420
|
+
useEffect22(() => {
|
|
8421
|
+
if (userToken) {
|
|
8422
|
+
cart.mergeGuestCart();
|
|
8423
|
+
}
|
|
8424
|
+
onUserTokenChange?.(!!userToken);
|
|
8425
|
+
}, [userToken]);
|
|
8426
|
+
return /* @__PURE__ */ jsx26(CartContext.Provider, { value: cart, children });
|
|
8427
|
+
}
|
|
8428
|
+
function useCartContext() {
|
|
8429
|
+
const context = useContext5(CartContext);
|
|
8430
|
+
if (!context) {
|
|
8431
|
+
throw new Error("useCartContext must be used within a CartProvider");
|
|
8432
|
+
}
|
|
8433
|
+
return context;
|
|
8434
|
+
}
|
|
8435
|
+
function useOptionalCartContext() {
|
|
8436
|
+
return useContext5(CartContext);
|
|
8437
|
+
}
|
|
8438
|
+
|
|
8439
|
+
// src/hooks/useCheckout.ts
|
|
8440
|
+
import { useState as useState21, useCallback as useCallback23, useRef as useRef23 } from "react";
|
|
8441
|
+
function getConfig3(options) {
|
|
8442
|
+
if (typeof window === "undefined") {
|
|
8443
|
+
throw new Error("useCheckout requires browser environment");
|
|
8444
|
+
}
|
|
8445
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
8446
|
+
const appId = options.appId || runtimeConfig?.appId;
|
|
8447
|
+
const apiUrl = options.apiUrl || runtimeConfig?.apiUrl;
|
|
8448
|
+
if (!appId) {
|
|
8449
|
+
throw new Error("App ID not configured (provide appId option or set YOAMIGO_CONFIG.appId)");
|
|
8450
|
+
}
|
|
8451
|
+
if (!apiUrl) {
|
|
8452
|
+
throw new Error("API URL not configured (provide apiUrl option or set YOAMIGO_CONFIG.apiUrl)");
|
|
8453
|
+
}
|
|
8454
|
+
return { appId, apiUrl };
|
|
8455
|
+
}
|
|
8456
|
+
function useCheckout(options = {}) {
|
|
8457
|
+
const { userToken } = options;
|
|
8458
|
+
const [isLoading, setIsLoading] = useState21(false);
|
|
8459
|
+
const [error, setError] = useState21(null);
|
|
8460
|
+
const configRef = useRef23(null);
|
|
8461
|
+
if (!configRef.current && typeof window !== "undefined") {
|
|
8462
|
+
try {
|
|
8463
|
+
configRef.current = getConfig3(options);
|
|
8464
|
+
} catch {
|
|
8465
|
+
}
|
|
8466
|
+
}
|
|
8467
|
+
const isAuthenticated = !!userToken;
|
|
8468
|
+
const sessionId = typeof window !== "undefined" ? getSessionId() : null;
|
|
8469
|
+
const initiateCheckout = useCallback23(async (checkoutOptions) => {
|
|
8470
|
+
const config = configRef.current;
|
|
8471
|
+
if (!config) {
|
|
8472
|
+
throw new Error("Checkout not configured");
|
|
8473
|
+
}
|
|
8474
|
+
setIsLoading(true);
|
|
8475
|
+
setError(null);
|
|
8476
|
+
try {
|
|
8477
|
+
const headers = {
|
|
8478
|
+
"Content-Type": "application/json"
|
|
8479
|
+
};
|
|
8480
|
+
if (isAuthenticated) {
|
|
8481
|
+
headers["X-App-User-Token"] = userToken;
|
|
8482
|
+
} else if (sessionId) {
|
|
8483
|
+
headers["X-Session-Id"] = sessionId;
|
|
8484
|
+
}
|
|
8485
|
+
const response = await fetch(
|
|
8486
|
+
`${config.apiUrl}/api/apps/${config.appId}/checkout/session`,
|
|
8487
|
+
{
|
|
8488
|
+
method: "POST",
|
|
8489
|
+
headers,
|
|
8490
|
+
body: JSON.stringify(checkoutOptions)
|
|
8491
|
+
}
|
|
8492
|
+
);
|
|
8493
|
+
const result = await response.json();
|
|
8494
|
+
if (!result.success || !result.data) {
|
|
8495
|
+
throw new Error(result.error || "Failed to create checkout session");
|
|
8496
|
+
}
|
|
8497
|
+
return result.data;
|
|
8498
|
+
} catch (err) {
|
|
8499
|
+
const message = err instanceof Error ? err.message : "Failed to initiate checkout";
|
|
8500
|
+
setError(message);
|
|
8501
|
+
throw err;
|
|
8502
|
+
} finally {
|
|
8503
|
+
setIsLoading(false);
|
|
8504
|
+
}
|
|
8505
|
+
}, [userToken, isAuthenticated, sessionId]);
|
|
8506
|
+
return {
|
|
8507
|
+
initiateCheckout,
|
|
8508
|
+
isLoading,
|
|
8509
|
+
error
|
|
8510
|
+
};
|
|
8511
|
+
}
|
|
8512
|
+
function useCheckoutStatus(options) {
|
|
8513
|
+
const { userToken, sessionId: checkoutSessionId, poll = false, pollInterval = 2e3 } = options;
|
|
8514
|
+
const [status, setStatus] = useState21(null);
|
|
8515
|
+
const [isLoading, setIsLoading] = useState21(true);
|
|
8516
|
+
const [error, setError] = useState21(null);
|
|
8517
|
+
const configRef = useRef23(null);
|
|
8518
|
+
if (!configRef.current && typeof window !== "undefined") {
|
|
8519
|
+
try {
|
|
8520
|
+
configRef.current = getConfig3(options);
|
|
8521
|
+
} catch {
|
|
8522
|
+
}
|
|
8523
|
+
}
|
|
8524
|
+
const isAuthenticated = !!userToken;
|
|
8525
|
+
const guestSessionId = typeof window !== "undefined" ? getSessionId() : null;
|
|
8526
|
+
const fetchStatus = useCallback23(async () => {
|
|
8527
|
+
const config = configRef.current;
|
|
8528
|
+
if (!config || !checkoutSessionId) {
|
|
8529
|
+
setIsLoading(false);
|
|
8530
|
+
return;
|
|
8531
|
+
}
|
|
8532
|
+
try {
|
|
8533
|
+
const headers = {
|
|
8534
|
+
"Content-Type": "application/json"
|
|
8535
|
+
};
|
|
8536
|
+
if (isAuthenticated) {
|
|
8537
|
+
headers["X-App-User-Token"] = userToken;
|
|
8538
|
+
} else if (guestSessionId) {
|
|
8539
|
+
headers["X-Session-Id"] = guestSessionId;
|
|
8540
|
+
}
|
|
8541
|
+
const response = await fetch(
|
|
8542
|
+
`${config.apiUrl}/api/apps/${config.appId}/checkout/session/${checkoutSessionId}/status`,
|
|
8543
|
+
{ headers }
|
|
8544
|
+
);
|
|
8545
|
+
const result = await response.json();
|
|
8546
|
+
if (result.success && result.data) {
|
|
8547
|
+
setStatus(result.data);
|
|
8548
|
+
setError(null);
|
|
8549
|
+
} else {
|
|
8550
|
+
setError(result.error || "Failed to fetch status");
|
|
8551
|
+
}
|
|
8552
|
+
} catch (err) {
|
|
8553
|
+
setError(err instanceof Error ? err.message : "Failed to fetch status");
|
|
8554
|
+
} finally {
|
|
8555
|
+
setIsLoading(false);
|
|
8556
|
+
}
|
|
8557
|
+
}, [checkoutSessionId, userToken, isAuthenticated, guestSessionId]);
|
|
8558
|
+
useState21(() => {
|
|
8559
|
+
fetchStatus();
|
|
8560
|
+
if (poll) {
|
|
8561
|
+
const interval = setInterval(() => {
|
|
8562
|
+
if (status?.paymentStatus === "paid" || status?.status === "expired") {
|
|
8563
|
+
return;
|
|
8564
|
+
}
|
|
8565
|
+
fetchStatus();
|
|
8566
|
+
}, pollInterval);
|
|
8567
|
+
return () => clearInterval(interval);
|
|
8568
|
+
}
|
|
8569
|
+
});
|
|
8570
|
+
return {
|
|
8571
|
+
status,
|
|
8572
|
+
isLoading,
|
|
8573
|
+
error,
|
|
8574
|
+
refresh: fetchStatus
|
|
8575
|
+
};
|
|
8576
|
+
}
|
|
8577
|
+
|
|
8578
|
+
// src/hooks/useProducts.ts
|
|
8579
|
+
function useProducts(options = {}) {
|
|
8580
|
+
const {
|
|
8581
|
+
collection = "products",
|
|
8582
|
+
activeOnly = true,
|
|
8583
|
+
category,
|
|
8584
|
+
productType,
|
|
8585
|
+
filters = [],
|
|
8586
|
+
page,
|
|
8587
|
+
pageSize,
|
|
8588
|
+
orderBy = "createdAt",
|
|
8589
|
+
orderDir = "desc",
|
|
8590
|
+
enabled
|
|
8591
|
+
} = options;
|
|
8592
|
+
const allFilters = [...filters];
|
|
8593
|
+
if (activeOnly) {
|
|
8594
|
+
allFilters.push({
|
|
8595
|
+
field: "status",
|
|
8596
|
+
operator: "eq",
|
|
8597
|
+
value: "active"
|
|
8598
|
+
});
|
|
8599
|
+
}
|
|
8600
|
+
if (category) {
|
|
8601
|
+
allFilters.push({
|
|
8602
|
+
field: "category",
|
|
8603
|
+
operator: "eq",
|
|
8604
|
+
value: category
|
|
8605
|
+
});
|
|
8606
|
+
}
|
|
8607
|
+
if (productType) {
|
|
8608
|
+
allFilters.push({
|
|
8609
|
+
field: "productType",
|
|
8610
|
+
operator: "eq",
|
|
8611
|
+
value: productType
|
|
8612
|
+
});
|
|
8613
|
+
}
|
|
8614
|
+
const result = useCollectionList({
|
|
8615
|
+
collection,
|
|
8616
|
+
filters: allFilters,
|
|
8617
|
+
page,
|
|
8618
|
+
pageSize,
|
|
8619
|
+
orderBy,
|
|
8620
|
+
orderDir,
|
|
8621
|
+
enabled
|
|
8622
|
+
});
|
|
8623
|
+
return {
|
|
8624
|
+
...result,
|
|
8625
|
+
products: result.data
|
|
8626
|
+
};
|
|
8627
|
+
}
|
|
8628
|
+
function useProduct(options) {
|
|
8629
|
+
const { collection = "products", slug, enabled = true } = options;
|
|
8630
|
+
const result = useCollectionList({
|
|
8631
|
+
collection,
|
|
8632
|
+
filters: [
|
|
8633
|
+
{ field: "slug", operator: "eq", value: slug }
|
|
8634
|
+
],
|
|
8635
|
+
pageSize: 1,
|
|
8636
|
+
enabled: enabled && !!slug
|
|
8637
|
+
});
|
|
8638
|
+
const product = result.data?.[0] || null;
|
|
8639
|
+
const notFound = !result.isLoading && !product && !result.error;
|
|
8640
|
+
return {
|
|
8641
|
+
product,
|
|
8642
|
+
isLoading: result.isLoading,
|
|
8643
|
+
error: result.error,
|
|
8644
|
+
notFound,
|
|
8645
|
+
refetch: result.refetch
|
|
8646
|
+
};
|
|
8647
|
+
}
|
|
8648
|
+
|
|
8649
|
+
// src/hooks/useSubscription.ts
|
|
8650
|
+
import { useState as useState22, useCallback as useCallback24, useRef as useRef24 } from "react";
|
|
8651
|
+
function getConfig4(options) {
|
|
8652
|
+
if (typeof window === "undefined") {
|
|
8653
|
+
throw new Error("useSubscription requires browser environment");
|
|
8654
|
+
}
|
|
8655
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
8656
|
+
const appId = options.appId || runtimeConfig?.appId;
|
|
8657
|
+
const apiUrl = options.apiUrl || runtimeConfig?.apiUrl;
|
|
8658
|
+
if (!appId) {
|
|
8659
|
+
throw new Error("App ID not configured (provide appId option or set YOAMIGO_CONFIG.appId)");
|
|
8660
|
+
}
|
|
8661
|
+
if (!apiUrl) {
|
|
8662
|
+
throw new Error("API URL not configured (provide apiUrl option or set YOAMIGO_CONFIG.apiUrl)");
|
|
8663
|
+
}
|
|
8664
|
+
return { appId, apiUrl };
|
|
8665
|
+
}
|
|
8666
|
+
function useSubscription(options = {}) {
|
|
8667
|
+
const { userToken } = options;
|
|
8668
|
+
const [isLoading, setIsLoading] = useState22(false);
|
|
8669
|
+
const [error, setError] = useState22(null);
|
|
8670
|
+
const configRef = useRef24(null);
|
|
8671
|
+
if (!configRef.current && typeof window !== "undefined") {
|
|
8672
|
+
try {
|
|
8673
|
+
configRef.current = getConfig4(options);
|
|
8674
|
+
} catch {
|
|
8675
|
+
}
|
|
8676
|
+
}
|
|
8677
|
+
const subscribe = useCallback24(async (params) => {
|
|
8678
|
+
const config = configRef.current;
|
|
8679
|
+
if (!config) {
|
|
8680
|
+
return {
|
|
8681
|
+
success: false,
|
|
8682
|
+
error: "Subscription not configured"
|
|
8683
|
+
};
|
|
8684
|
+
}
|
|
8685
|
+
setIsLoading(true);
|
|
8686
|
+
setError(null);
|
|
8687
|
+
try {
|
|
8688
|
+
const headers = {
|
|
8689
|
+
"Content-Type": "application/json"
|
|
8690
|
+
};
|
|
8691
|
+
if (userToken) {
|
|
8692
|
+
headers["X-App-User-Token"] = userToken;
|
|
8693
|
+
}
|
|
8694
|
+
const response = await fetch(
|
|
8695
|
+
`${config.apiUrl}/api/apps/${config.appId}/subscriptions/checkout`,
|
|
8696
|
+
{
|
|
8697
|
+
method: "POST",
|
|
8698
|
+
headers,
|
|
8699
|
+
body: JSON.stringify({
|
|
8700
|
+
tierSlug: params.tierSlug,
|
|
8701
|
+
successUrl: params.successUrl,
|
|
8702
|
+
cancelUrl: params.cancelUrl,
|
|
8703
|
+
quantity: params.quantity,
|
|
8704
|
+
trialDays: params.trialDays,
|
|
8705
|
+
customerEmail: params.customerEmail,
|
|
8706
|
+
metadata: params.metadata
|
|
8707
|
+
})
|
|
8708
|
+
}
|
|
8709
|
+
);
|
|
8710
|
+
const result = await response.json();
|
|
8711
|
+
if (!result.success || !result.data) {
|
|
8712
|
+
const errorMessage = result.error || "Failed to create subscription checkout";
|
|
8713
|
+
setError(errorMessage);
|
|
8714
|
+
return {
|
|
8715
|
+
success: false,
|
|
8716
|
+
error: errorMessage
|
|
8717
|
+
};
|
|
8718
|
+
}
|
|
8719
|
+
return {
|
|
8720
|
+
success: true,
|
|
8721
|
+
checkoutUrl: result.data.url,
|
|
8722
|
+
sessionId: result.data.sessionId
|
|
8723
|
+
};
|
|
8724
|
+
} catch (err) {
|
|
8725
|
+
const message = err instanceof Error ? err.message : "Failed to create subscription checkout";
|
|
8726
|
+
setError(message);
|
|
8727
|
+
return {
|
|
8728
|
+
success: false,
|
|
8729
|
+
error: message
|
|
8730
|
+
};
|
|
8731
|
+
} finally {
|
|
8732
|
+
setIsLoading(false);
|
|
8733
|
+
}
|
|
8734
|
+
}, [userToken]);
|
|
8735
|
+
return {
|
|
8736
|
+
subscribe,
|
|
8737
|
+
isLoading,
|
|
8738
|
+
error
|
|
8739
|
+
};
|
|
8740
|
+
}
|
|
8741
|
+
|
|
8742
|
+
// src/hooks/useSubscriptionTiers.ts
|
|
8743
|
+
import { useState as useState23, useCallback as useCallback25, useEffect as useEffect23, useRef as useRef25 } from "react";
|
|
8744
|
+
function getConfig5(options) {
|
|
8745
|
+
if (typeof window === "undefined") {
|
|
8746
|
+
throw new Error("useSubscriptionTiers requires browser environment");
|
|
8747
|
+
}
|
|
8748
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
8749
|
+
const appId = options.appId || runtimeConfig?.appId;
|
|
8750
|
+
const apiUrl = options.apiUrl || runtimeConfig?.apiUrl;
|
|
8751
|
+
if (!appId) {
|
|
8752
|
+
throw new Error(
|
|
8753
|
+
"App ID not configured (provide appId option or set YOAMIGO_CONFIG.appId)"
|
|
8754
|
+
);
|
|
8755
|
+
}
|
|
8756
|
+
if (!apiUrl) {
|
|
8757
|
+
throw new Error(
|
|
8758
|
+
"API URL not configured (provide apiUrl option or set YOAMIGO_CONFIG.apiUrl)"
|
|
8759
|
+
);
|
|
8760
|
+
}
|
|
8761
|
+
return { appId, apiUrl };
|
|
8762
|
+
}
|
|
8763
|
+
function useSubscriptionTiers(options = {}) {
|
|
8764
|
+
const { fetchOnMount = true } = options;
|
|
8765
|
+
const [tiers, setTiers] = useState23([]);
|
|
8766
|
+
const [isLoading, setIsLoading] = useState23(false);
|
|
8767
|
+
const [error, setError] = useState23(null);
|
|
8768
|
+
const configRef = useRef25(null);
|
|
8769
|
+
if (!configRef.current && typeof window !== "undefined") {
|
|
8770
|
+
try {
|
|
8771
|
+
configRef.current = getConfig5(options);
|
|
8772
|
+
} catch {
|
|
8773
|
+
}
|
|
8774
|
+
}
|
|
8775
|
+
const refresh = useCallback25(async () => {
|
|
8776
|
+
const config = configRef.current;
|
|
8777
|
+
if (!config) {
|
|
8778
|
+
setError("Subscription tiers not configured");
|
|
8779
|
+
return;
|
|
8780
|
+
}
|
|
8781
|
+
setIsLoading(true);
|
|
8782
|
+
setError(null);
|
|
8783
|
+
try {
|
|
8784
|
+
const response = await fetch(
|
|
8785
|
+
`${config.apiUrl}/api/apps/${config.appId}/subscriptions/tiers`,
|
|
8786
|
+
{
|
|
8787
|
+
method: "GET",
|
|
8788
|
+
headers: {
|
|
8789
|
+
"Content-Type": "application/json"
|
|
8790
|
+
}
|
|
8791
|
+
}
|
|
8792
|
+
);
|
|
8793
|
+
const result = await response.json();
|
|
8794
|
+
if (!result.success || !result.data) {
|
|
8795
|
+
const errorMessage = result.error || "Failed to fetch subscription tiers";
|
|
8796
|
+
setError(errorMessage);
|
|
8797
|
+
return;
|
|
8798
|
+
}
|
|
8799
|
+
setTiers(result.data.tiers);
|
|
8800
|
+
} catch (err) {
|
|
8801
|
+
const message = err instanceof Error ? err.message : "Failed to fetch subscription tiers";
|
|
8802
|
+
setError(message);
|
|
8803
|
+
} finally {
|
|
8804
|
+
setIsLoading(false);
|
|
8805
|
+
}
|
|
8806
|
+
}, []);
|
|
8807
|
+
useEffect23(() => {
|
|
8808
|
+
if (fetchOnMount && configRef.current) {
|
|
8809
|
+
refresh();
|
|
8810
|
+
}
|
|
8811
|
+
}, [fetchOnMount, refresh]);
|
|
8812
|
+
return {
|
|
8813
|
+
tiers,
|
|
8814
|
+
isLoading,
|
|
8815
|
+
error,
|
|
8816
|
+
refresh
|
|
8817
|
+
};
|
|
8818
|
+
}
|
|
8819
|
+
|
|
8820
|
+
// src/hooks/useCustomerPortal.ts
|
|
8821
|
+
import { useState as useState24, useCallback as useCallback26, useRef as useRef26 } from "react";
|
|
8822
|
+
function getConfig6(options) {
|
|
8823
|
+
if (typeof window === "undefined") {
|
|
8824
|
+
throw new Error("useCustomerPortal requires browser environment");
|
|
8825
|
+
}
|
|
8826
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
8827
|
+
const appId = options.appId || runtimeConfig?.appId;
|
|
8828
|
+
const apiUrl = options.apiUrl || runtimeConfig?.apiUrl;
|
|
8829
|
+
if (!appId) {
|
|
8830
|
+
throw new Error("App ID not configured (provide appId option or set YOAMIGO_CONFIG.appId)");
|
|
8831
|
+
}
|
|
8832
|
+
if (!apiUrl) {
|
|
8833
|
+
throw new Error("API URL not configured (provide apiUrl option or set YOAMIGO_CONFIG.apiUrl)");
|
|
8834
|
+
}
|
|
8835
|
+
return { appId, apiUrl };
|
|
8836
|
+
}
|
|
8837
|
+
function useCustomerPortal(options) {
|
|
8838
|
+
const { userToken } = options;
|
|
8839
|
+
const [isLoading, setIsLoading] = useState24(false);
|
|
8840
|
+
const [error, setError] = useState24(null);
|
|
8841
|
+
const configRef = useRef26(null);
|
|
8842
|
+
if (!configRef.current && typeof window !== "undefined") {
|
|
8843
|
+
try {
|
|
8844
|
+
configRef.current = getConfig6(options);
|
|
8845
|
+
} catch {
|
|
8846
|
+
}
|
|
8847
|
+
}
|
|
8848
|
+
const openPortal = useCallback26(async (params) => {
|
|
8849
|
+
const config = configRef.current;
|
|
8850
|
+
if (!config) {
|
|
8851
|
+
return {
|
|
8852
|
+
success: false,
|
|
8853
|
+
error: "Customer portal not configured"
|
|
8854
|
+
};
|
|
8855
|
+
}
|
|
8856
|
+
if (!userToken) {
|
|
8857
|
+
return {
|
|
8858
|
+
success: false,
|
|
8859
|
+
error: "Authentication required to access customer portal"
|
|
8860
|
+
};
|
|
8861
|
+
}
|
|
8862
|
+
setIsLoading(true);
|
|
8863
|
+
setError(null);
|
|
8864
|
+
try {
|
|
8865
|
+
const response = await fetch(
|
|
8866
|
+
`${config.apiUrl}/api/apps/${config.appId}/subscriptions/portal`,
|
|
8867
|
+
{
|
|
8868
|
+
method: "POST",
|
|
8869
|
+
headers: {
|
|
8870
|
+
"Content-Type": "application/json",
|
|
8871
|
+
"X-App-User-Token": userToken
|
|
8872
|
+
},
|
|
8873
|
+
body: JSON.stringify({
|
|
8874
|
+
returnUrl: params.returnUrl
|
|
8875
|
+
})
|
|
8876
|
+
}
|
|
8877
|
+
);
|
|
8878
|
+
const result = await response.json();
|
|
8879
|
+
if (!result.success || !result.data) {
|
|
8880
|
+
const errorMessage = result.error || "Failed to create portal session";
|
|
8881
|
+
setError(errorMessage);
|
|
8882
|
+
return {
|
|
8883
|
+
success: false,
|
|
8884
|
+
error: errorMessage
|
|
8885
|
+
};
|
|
8886
|
+
}
|
|
8887
|
+
return {
|
|
8888
|
+
success: true,
|
|
8889
|
+
portalUrl: result.data.portalUrl
|
|
8890
|
+
};
|
|
8891
|
+
} catch (err) {
|
|
8892
|
+
const message = err instanceof Error ? err.message : "Failed to create portal session";
|
|
8893
|
+
setError(message);
|
|
8894
|
+
return {
|
|
8895
|
+
success: false,
|
|
8896
|
+
error: message
|
|
8897
|
+
};
|
|
8898
|
+
} finally {
|
|
8899
|
+
setIsLoading(false);
|
|
8900
|
+
}
|
|
8901
|
+
}, [userToken]);
|
|
8902
|
+
return {
|
|
8903
|
+
openPortal,
|
|
8904
|
+
isLoading,
|
|
8905
|
+
error
|
|
8906
|
+
};
|
|
8907
|
+
}
|
|
8908
|
+
|
|
8909
|
+
// src/hooks/useSubscriptionStatus.ts
|
|
8910
|
+
import { useState as useState25, useCallback as useCallback27, useRef as useRef27, useEffect as useEffect24 } from "react";
|
|
8911
|
+
function getConfig7(options) {
|
|
8912
|
+
if (typeof window === "undefined") {
|
|
8913
|
+
throw new Error("useSubscriptionStatus requires browser environment");
|
|
8914
|
+
}
|
|
8915
|
+
const runtimeConfig = window.YOAMIGO_CONFIG;
|
|
8916
|
+
const appId = options.appId || runtimeConfig?.appId;
|
|
8917
|
+
const apiUrl = options.apiUrl || runtimeConfig?.apiUrl;
|
|
8918
|
+
if (!appId) {
|
|
8919
|
+
throw new Error("App ID not configured (provide appId option or set YOAMIGO_CONFIG.appId)");
|
|
8920
|
+
}
|
|
8921
|
+
if (!apiUrl) {
|
|
8922
|
+
throw new Error("API URL not configured (provide apiUrl option or set YOAMIGO_CONFIG.apiUrl)");
|
|
8923
|
+
}
|
|
8924
|
+
return { appId, apiUrl };
|
|
8925
|
+
}
|
|
8926
|
+
function useSubscriptionStatus(options) {
|
|
8927
|
+
const { userToken, autoFetch = true, pollInterval = 0 } = options;
|
|
8928
|
+
const [status, setStatus] = useState25(null);
|
|
8929
|
+
const [subscriptions, setSubscriptions] = useState25([]);
|
|
8930
|
+
const [isLoading, setIsLoading] = useState25(autoFetch);
|
|
8931
|
+
const [error, setError] = useState25(null);
|
|
8932
|
+
const configRef = useRef27(null);
|
|
8933
|
+
if (!configRef.current && typeof window !== "undefined") {
|
|
8934
|
+
try {
|
|
8935
|
+
configRef.current = getConfig7(options);
|
|
8936
|
+
} catch {
|
|
8937
|
+
}
|
|
8938
|
+
}
|
|
8939
|
+
const fetchStatus = useCallback27(async () => {
|
|
8940
|
+
const config = configRef.current;
|
|
8941
|
+
if (!config) {
|
|
8942
|
+
setError("Subscription status not configured");
|
|
8943
|
+
setIsLoading(false);
|
|
8944
|
+
return;
|
|
8945
|
+
}
|
|
8946
|
+
if (!userToken) {
|
|
8947
|
+
setStatus("none");
|
|
8948
|
+
setSubscriptions([]);
|
|
8949
|
+
setIsLoading(false);
|
|
8950
|
+
return;
|
|
8951
|
+
}
|
|
8952
|
+
setIsLoading(true);
|
|
8953
|
+
try {
|
|
8954
|
+
const response = await fetch(
|
|
8955
|
+
`${config.apiUrl}/api/apps/${config.appId}/subscriptions/status`,
|
|
8956
|
+
{
|
|
8957
|
+
method: "GET",
|
|
8958
|
+
headers: {
|
|
8959
|
+
"Content-Type": "application/json",
|
|
8960
|
+
"X-App-User-Token": userToken
|
|
8961
|
+
}
|
|
8962
|
+
}
|
|
8963
|
+
);
|
|
8964
|
+
const result = await response.json();
|
|
8965
|
+
if (!result.success) {
|
|
8966
|
+
setError(result.error || "Failed to fetch subscription status");
|
|
8967
|
+
return;
|
|
8968
|
+
}
|
|
8969
|
+
setStatus(result.data?.status ?? "none");
|
|
8970
|
+
setSubscriptions(result.data?.subscriptions ?? []);
|
|
8971
|
+
setError(null);
|
|
8972
|
+
} catch (err) {
|
|
8973
|
+
setError(err instanceof Error ? err.message : "Failed to fetch subscription status");
|
|
8974
|
+
} finally {
|
|
8975
|
+
setIsLoading(false);
|
|
8976
|
+
}
|
|
8977
|
+
}, [userToken]);
|
|
8978
|
+
useEffect24(() => {
|
|
8979
|
+
if (autoFetch && userToken) {
|
|
8980
|
+
fetchStatus();
|
|
8981
|
+
}
|
|
8982
|
+
}, [autoFetch, userToken, fetchStatus]);
|
|
8983
|
+
useEffect24(() => {
|
|
8984
|
+
if (pollInterval <= 0 || !userToken) {
|
|
8985
|
+
return;
|
|
8986
|
+
}
|
|
8987
|
+
const interval = setInterval(() => {
|
|
8988
|
+
fetchStatus();
|
|
8989
|
+
}, pollInterval);
|
|
8990
|
+
return () => clearInterval(interval);
|
|
8991
|
+
}, [pollInterval, userToken, fetchStatus]);
|
|
8992
|
+
return {
|
|
8993
|
+
status,
|
|
8994
|
+
subscriptions,
|
|
8995
|
+
isLoading,
|
|
8996
|
+
error,
|
|
8997
|
+
refresh: fetchStatus
|
|
8998
|
+
};
|
|
8999
|
+
}
|
|
7094
9000
|
export {
|
|
7095
9001
|
AIEditProvider,
|
|
9002
|
+
CartProvider,
|
|
9003
|
+
CollectionClient,
|
|
9004
|
+
CollectionContentProvider,
|
|
9005
|
+
CollectionDetailPage,
|
|
9006
|
+
CollectionItem,
|
|
9007
|
+
CollectionList,
|
|
9008
|
+
CollectionListPage,
|
|
7096
9009
|
ContentStoreProvider,
|
|
7097
9010
|
ContentStoreProvider2 as ContentStoreProviderProd,
|
|
7098
9011
|
Link2 as Link,
|
|
@@ -7115,12 +9028,21 @@ export {
|
|
|
7115
9028
|
background,
|
|
7116
9029
|
buildIntermediateText,
|
|
7117
9030
|
calculateAnimationTiming,
|
|
9031
|
+
clearSessionId,
|
|
7118
9032
|
computeTextDiff,
|
|
7119
9033
|
containsHtml,
|
|
7120
9034
|
contentRegistry,
|
|
9035
|
+
createRouteDefinition,
|
|
7121
9036
|
embed,
|
|
9037
|
+
extractRouteParams,
|
|
9038
|
+
filePathToRoutePath,
|
|
9039
|
+
generatePath,
|
|
7122
9040
|
getAllContent,
|
|
9041
|
+
getCollectionClient,
|
|
7123
9042
|
getContent,
|
|
9043
|
+
getLocalCartItemCount,
|
|
9044
|
+
getLocalCartItems,
|
|
9045
|
+
getSessionId,
|
|
7124
9046
|
getTextCursorPosition,
|
|
7125
9047
|
hasContent,
|
|
7126
9048
|
image,
|
|
@@ -7129,13 +9051,17 @@ export {
|
|
|
7129
9051
|
linkTransitionStrategy,
|
|
7130
9052
|
parseBackgroundConfig,
|
|
7131
9053
|
parseEmbedUrl,
|
|
9054
|
+
parseFieldValue,
|
|
7132
9055
|
registerContent,
|
|
9056
|
+
resetCollectionClient,
|
|
7133
9057
|
resolveAssetUrl,
|
|
7134
9058
|
serializeBackgroundConfig,
|
|
7135
9059
|
serializeEmbedValue,
|
|
7136
9060
|
serializeImageValue,
|
|
7137
9061
|
serializeVideoValue,
|
|
7138
9062
|
setAssetResolver,
|
|
9063
|
+
sortRoutesBySpecificity,
|
|
9064
|
+
stringifyFieldValue,
|
|
7139
9065
|
stripHtml,
|
|
7140
9066
|
text,
|
|
7141
9067
|
textTypingStrategy,
|
|
@@ -7143,10 +9069,26 @@ export {
|
|
|
7143
9069
|
useAIEditContext,
|
|
7144
9070
|
useAIEditContextOptional,
|
|
7145
9071
|
useAnimatedText,
|
|
9072
|
+
useCart,
|
|
9073
|
+
useCartContext,
|
|
9074
|
+
useCheckout,
|
|
9075
|
+
useCheckoutStatus,
|
|
9076
|
+
useCollectionContent,
|
|
9077
|
+
useCollectionList,
|
|
9078
|
+
useCollectionRecord,
|
|
9079
|
+
useContent,
|
|
7146
9080
|
useContentStore,
|
|
7147
9081
|
useContentStore2 as useContentStoreProd,
|
|
9082
|
+
useCustomerPortal,
|
|
9083
|
+
useLocation4 as useLocation,
|
|
7148
9084
|
useNavigate,
|
|
7149
|
-
|
|
9085
|
+
useOptionalCartContext,
|
|
9086
|
+
useParams2 as useParams,
|
|
9087
|
+
useProduct,
|
|
9088
|
+
useProducts,
|
|
7150
9089
|
useSafeTriangle,
|
|
9090
|
+
useSubscription,
|
|
9091
|
+
useSubscriptionStatus,
|
|
9092
|
+
useSubscriptionTiers,
|
|
7151
9093
|
video
|
|
7152
9094
|
};
|