@guidelytech/guide-runtime 0.2.1 → 0.4.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/index.cjs CHANGED
@@ -36,9 +36,9 @@ __export(src_exports, {
36
36
  generateSelectors: () => generateSelectors,
37
37
  highlightSelectors: () => highlightSelectors,
38
38
  isAnchoredStep: () => isAnchoredStep,
39
- isChecklistComplete: () => isChecklistComplete,
39
+ isChecklistComplete: () => import_runtime4.isChecklistComplete,
40
40
  isGuidelyElement: () => isGuidelyElement,
41
- isQuestionVisible: () => isQuestionVisible,
41
+ isQuestionVisible: () => import_runtime5.isQuestionVisible,
42
42
  isStableId: () => isStableId,
43
43
  isVisible: () => isVisible,
44
44
  normalizeText: () => normalizeText,
@@ -165,6 +165,10 @@ function querySelectorStrategy(strategy, root) {
165
165
  const id = value.startsWith("#") ? value.slice(1) : value;
166
166
  return safeQueryAll(root, `#${escapeCss(id)}`);
167
167
  }
168
+ case "guidely-id": {
169
+ const id = value.startsWith("#") ? value.slice(1) : value;
170
+ return safeQueryAll(root, attrSelector("data-guidely-id", id));
171
+ }
168
172
  case "data-attr":
169
173
  case "aria":
170
174
  case "css":
@@ -226,7 +230,7 @@ function waitForTarget(target, options = {}) {
226
230
  const observeRoot = root.nodeType === 9 ? root.documentElement : root;
227
231
  if (typeof MutationObserver !== "undefined" && observeRoot) {
228
232
  observer = new MutationObserver(check);
229
- observer.observe(observeRoot, { childList: true, subtree: true, attributes: true, attributeFilter: ["class", "style", "hidden", "id", "data-tour", "aria-label"] });
233
+ observer.observe(observeRoot, { childList: true, subtree: true, attributes: true, attributeFilter: ["class", "style", "hidden", "id", "data-tour", "data-guidely-id", "aria-label"] });
230
234
  }
231
235
  timer = setInterval(check, pollMs);
232
236
  if (timeoutMs > 0) {
@@ -240,7 +244,7 @@ function waitForTarget(target, options = {}) {
240
244
  var GENERATED_ID = /(\d{4,}|:r[0-9a-z]+:|^radix-|^headlessui-|^mui-|^ember\d|^react-|^\w*-\d+$|__|^[a-f0-9]{8,}$|^svelte-)/i;
241
245
  var GENERATED_CLASS = /(^css-|^sc-|^jss\d|^svelte-|^emotion-|^chakra-|^Mui|^_|[0-9a-f]{6,}|^\w{1,2}$|^[a-z]+-\d+$|[:[\]/])/;
242
246
  var PREFERRED_DATA_ATTRS = ["data-testid", "data-tour", "data-test", "data-test-id", "data-cy", "data-qa", "data-id", "data-name", "data-guidely"];
243
- var IGNORED_DATA_ATTRS = /^data-(state|v-|reactid|react|radix|headlessui|svelte|hydrated|orientation|highlighted|focus|key|index|rbd|dnd|rk-|sveltekit)/i;
247
+ var IGNORED_DATA_ATTRS = /^data-(guidely-id|state|v-|reactid|react|radix|headlessui|svelte|hydrated|orientation|highlighted|focus|key|index|rbd|dnd|rk-|sveltekit)/i;
244
248
  function isStableId(id) {
245
249
  return !!id && /^[A-Za-z][\w-]*$/.test(id) && !GENERATED_ID.test(id);
246
250
  }
@@ -298,6 +302,8 @@ function generateSelectors(el2) {
298
302
  }
299
303
  };
300
304
  const tag = el2.tagName.toLowerCase();
305
+ const guidelyId = el2.getAttribute("data-guidely-id");
306
+ if (guidelyId && uniqueIn(doc, attrSelector("data-guidely-id", guidelyId), el2)) push({ kind: "guidely-id", value: guidelyId, label: `Guidely id ${guidelyId}` });
301
307
  if (isStableId(el2.id) && uniqueIn(doc, `#${escapeCss(el2.id)}`, el2)) push({ kind: "id", value: `#${el2.id}`, label: `id ${el2.id}` });
302
308
  const dataAttrs = Array.from(el2.attributes).filter((a) => a.name.startsWith("data-") && !IGNORED_DATA_ATTRS.test(a.name) && a.value && a.value.length <= 120 && !/\d{5,}/.test(a.value));
303
309
  dataAttrs.sort((a, b) => {
@@ -1489,10 +1495,7 @@ function runGuide(guide, options = {}) {
1489
1495
  }
1490
1496
 
1491
1497
  // src/checklist.ts
1492
- function isChecklistComplete(config, progress) {
1493
- const done = new Set(progress.completedTaskIds);
1494
- return config.tasks.filter((t) => t.required).every((t) => done.has(t.id));
1495
- }
1498
+ var import_runtime4 = require("@guidelytech/shared/runtime");
1496
1499
  function renderChecklist(options) {
1497
1500
  const { scope, config } = options;
1498
1501
  const doc = scope.ownerDocument;
@@ -1567,7 +1570,7 @@ function renderChecklist(options) {
1567
1570
  bar.setAttribute("aria-valuemax", "100");
1568
1571
  body.textContent = "";
1569
1572
  foot.textContent = "";
1570
- const complete = isChecklistComplete(config, progress);
1573
+ const complete = (0, import_runtime4.isChecklistComplete)(config, progress);
1571
1574
  if (complete) {
1572
1575
  const wrap = doc.createElement("div");
1573
1576
  wrap.className = "g-complete";
@@ -1655,26 +1658,7 @@ function renderChecklist(options) {
1655
1658
  }
1656
1659
 
1657
1660
  // src/survey.ts
1658
- function isQuestionVisible(q, answers) {
1659
- if (!q.showWhen) return true;
1660
- const actual = answers[q.showWhen.questionId];
1661
- if (actual === void 0) return false;
1662
- const expected = q.showWhen.value;
1663
- switch (q.showWhen.op) {
1664
- case "equals":
1665
- return Array.isArray(actual) ? actual.includes(String(expected)) : String(actual) === String(expected);
1666
- case "lte":
1667
- return Number(actual) <= Number(expected);
1668
- case "gte":
1669
- return Number(actual) >= Number(expected);
1670
- case "in": {
1671
- const list = Array.isArray(expected) ? expected : [String(expected)];
1672
- return Array.isArray(actual) ? actual.some((a) => list.includes(a)) : list.includes(String(actual));
1673
- }
1674
- default:
1675
- return false;
1676
- }
1677
- }
1661
+ var import_runtime5 = require("@guidelytech/shared/runtime");
1678
1662
  function renderSurvey(options) {
1679
1663
  const { scope, config } = options;
1680
1664
  const doc = scope.ownerDocument;
@@ -1866,14 +1850,14 @@ function renderSurvey(options) {
1866
1850
  config.questions.forEach((q) => {
1867
1851
  const el2 = questionEls.get(q.id);
1868
1852
  if (!el2) return;
1869
- const visible = isQuestionVisible(q, answers);
1853
+ const visible = (0, import_runtime5.isQuestionVisible)(q, answers);
1870
1854
  el2.hidden = !visible;
1871
1855
  if (!visible && answers[q.id] !== void 0) delete answers[q.id];
1872
1856
  });
1873
1857
  }
1874
1858
  form.addEventListener("submit", (e) => {
1875
1859
  e.preventDefault();
1876
- const missing = config.questions.filter((q) => q.required && isQuestionVisible(q, answers) && answers[q.id] === void 0);
1860
+ const missing = config.questions.filter((q) => q.required && (0, import_runtime5.isQuestionVisible)(q, answers) && answers[q.id] === void 0);
1877
1861
  if (missing.length) {
1878
1862
  errorEl.textContent = `Please answer: ${missing.map((q) => q.title).join(", ")}`;
1879
1863
  questionEls.get(missing[0].id)?.querySelector("button,textarea")?.focus();
@@ -2349,7 +2333,7 @@ function previewStep(step, theme, options = {}) {
2349
2333
  name: "Preview",
2350
2334
  kind: "flow",
2351
2335
  version: 0,
2352
- config: { steps: [previewStepConfig], targeting: { id: "root", op: "and", rules: [] }, trigger: { type: "manual", delayMs: 0 }, frequency: { mode: "every_time", cooldownHours: 0, priority: 0, mutuallyExclusiveWith: [], reshowAfterDismiss: true }, appearance: options.appearance ?? { theme: "auto", animation: "fade" }, urlPatterns: [] }
2336
+ config: { steps: [previewStepConfig], targeting: { id: "root", op: "and", rules: [] }, trigger: { type: "manual", delayMs: 0 }, frequency: { mode: "every_time", cooldownHours: 0, priority: 0, mutuallyExclusiveWith: [], reshowAfterDismiss: true }, appearance: options.appearance ?? { theme: "auto", animation: "fade" }, urlPatterns: [], platforms: ["web"] }
2353
2337
  };
2354
2338
  const controller = runGuide(guide, { theme, doc: options.doc, onEnd: () => options.onEnd?.(), onNavigate: () => {
2355
2339
  }, onAction: () => {