@cocreate/utils 1.37.2 → 1.38.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +1 -1
  3. package/src/index.js +187 -124
package/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ # [1.38.0](https://github.com/CoCreate-app/CoCreate-utils/compare/v1.37.3...v1.38.0) (2025-04-11)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * query elements ([663eec4](https://github.com/CoCreate-app/CoCreate-utils/commit/663eec46c92716c298e45cd4fa6ec48be616a9a1))
7
+ * Selector === "" retuning null ([e8fd9fb](https://github.com/CoCreate-app/CoCreate-utils/commit/e8fd9fb726f78477e9ea5a15e9bf08097bc9250f))
8
+ * special selector split ([94dd267](https://github.com/CoCreate-app/CoCreate-utils/commit/94dd267d4028945c9813c4bc7be33bcc7d0494d3))
9
+ * variable typo selector ([fe101f5](https://github.com/CoCreate-app/CoCreate-utils/commit/fe101f5a9c728d054466b21524cfc2b0ece07e8a))
10
+
11
+
12
+ ### Features
13
+
14
+ * improve queryElements() ([0e05425](https://github.com/CoCreate-app/CoCreate-utils/commit/0e05425fc68394004ef2c78f3bd2004447b2233a))
15
+ * prefix-document to query document ([21644d9](https://github.com/CoCreate-app/CoCreate-utils/commit/21644d940e1f20530f5594b751d596b4aa431fc4))
16
+
17
+ ## [1.37.3](https://github.com/CoCreate-app/CoCreate-utils/compare/v1.37.2...v1.37.3) (2024-12-22)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * handling updates for arrays using dotnotation ([e227054](https://github.com/CoCreate-app/CoCreate-utils/commit/e2270546f349a1a86f2d858e6e779c98b5cc9a7f))
23
+
1
24
  ## [1.37.2](https://github.com/CoCreate-app/CoCreate-utils/compare/v1.37.1...v1.37.2) (2024-12-14)
2
25
 
3
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/utils",
3
- "version": "1.37.2",
3
+ "version": "1.38.0",
4
4
  "description": "A simple utils component in vanilla javascript. Easily configured using HTML5 attributes and/or JavaScript API.",
5
5
  "keywords": [
6
6
  "utils",
package/src/index.js CHANGED
@@ -290,14 +290,17 @@
290
290
  for (const key of Object.keys(data)) {
291
291
  let newObject = object;
292
292
  let oldObject = new Object(newObject);
293
- let keys = key.replace(/\[(\d+)\]/g, ".$1").split(".");
293
+ let keys = key
294
+ .replace(/\[(\d+)\]/g, ".$1")
295
+ .split(".")
296
+ .map((k) => (isNaN(k) ? k : Number(k)));
294
297
  let value = data[key];
295
298
  let operator;
296
299
  if (keys[0].startsWith("$")) operator = keys.shift();
297
300
 
298
301
  let length = keys.length - 1;
299
302
  for (let i = 0; i < keys.length; i++) {
300
- if (/^\d+$/.test(keys[i])) keys[i] = parseInt(keys[i]);
303
+ // if (/^\d+$/.test(keys[i])) keys[i] = parseInt(keys[i]);
301
304
 
302
305
  if (length == i) {
303
306
  if (operator) {
@@ -411,12 +414,22 @@
411
414
  if (typeof keys[i] === "number")
412
415
  newObject.splice(keys[i], 1);
413
416
  else delete newObject[keys[i]];
414
- } else newObject[keys[i]] = value;
417
+ } else if (typeof keys[i] === "number") {
418
+ newObject.splice(keys[i], 0, value);
419
+ } else {
420
+ newObject[keys[i]] = value;
421
+ }
422
+ } else if (
423
+ typeof keys[i + 1] === "number" &&
424
+ !Array.isArray(newObject[keys[i]])
425
+ ) {
426
+ newObject[keys[i]] = [];
415
427
  } else {
416
- newObject[keys[i]] = oldObject[keys[i]] || {};
417
- newObject = newObject[keys[i]];
418
- oldObject = oldObject[keys[i]];
428
+ newObject[keys[i]] = newObject[keys[i]] || {};
429
+ // newObject[keys[i]] = oldObject[keys[i]] || {};
430
+ // oldObject = oldObject[keys[i]];
419
431
  }
432
+ newObject = newObject[keys[i]];
420
433
  }
421
434
  }
422
435
  return object;
@@ -527,131 +540,142 @@
527
540
  return path;
528
541
  }
529
542
 
530
- function queryElements({ element, prefix, type, selector }) {
531
- let elements = new Map();
532
- if (!element) element = document;
543
+ const queryTypes = [
544
+ "selector",
545
+ "closest",
546
+ "parent",
547
+ "next",
548
+ "previous",
549
+ "document",
550
+ "frame",
551
+ "top"
552
+ ];
553
+
554
+ const queryTypesRegex = new RegExp(`\\$(?:${queryTypes.join("|")})\\b`); // Find the *first* match
555
+
556
+ function queryElements({ element = document, prefix, type, selector }) {
557
+ let elements = new Set();
558
+
533
559
  let hasAttribute = false;
534
560
 
535
- if (selector) {
536
- if (!type) type = ["selector"];
537
- else if (!Array.isArray(type)) type = [type];
538
- } else type = ["selector", "closest", "parent", "next", "previous"];
561
+ if (!selector) {
562
+ if (!prefix && element.nodeType === 1) {
563
+ for (let attr of element.attributes) {
564
+ let parts = attr.name.split("-");
565
+ if (parts.length < 2) continue;
566
+
567
+ let possibleType = parts.pop();
568
+ if (queryTypes.includes(possibleType)) {
569
+ type = [possibleType];
570
+ prefix = parts.join("-");
571
+ break;
572
+ }
573
+ }
574
+ if (!prefix) return false;
575
+ } else if (!type && element.nodeType === 1) {
576
+ for (let i = 0; i < queryTypes.length; i++) {
577
+ if (element.hasAttribute(`${prefix}-${queryTypes[i]}`)) {
578
+ type = [queryTypes[i]];
579
+ }
580
+ }
581
+ }
582
+ }
583
+
584
+ if (!type) type = selector ? ["selector"] : queryTypes;
585
+
586
+ if (!Array.isArray(type)) type = [type];
539
587
 
540
588
  for (let i = 0; i < type.length; i++) {
541
- let Selector = selector;
542
- if (!Selector && element.nodeType !== 9) {
589
+ if (!selector && element.nodeType !== 9) {
543
590
  let name = prefix + "-" + type[i];
544
591
  if (!element.hasAttribute(name)) continue;
545
592
  hasAttribute = true;
546
- Selector = element.getAttribute(name);
593
+ selector = element.getAttribute(name);
594
+ type = [type[i]];
547
595
  }
548
596
 
549
- if (Selector) {
550
- let selectors = Selector.split(/,(?![^()]*\))/g);
597
+ let mainElement = element;
598
+ if (
599
+ [
600
+ "parent",
601
+ "next",
602
+ "previous",
603
+ "document",
604
+ "frame",
605
+ "top"
606
+ ].includes(type[i])
607
+ ) {
608
+ mainElement = queryType(mainElement, type[i]);
609
+ }
610
+
611
+ if (!selector) {
612
+ elements.add(mainElement);
613
+ } else {
614
+ let selectors = selector.split(/,(?![^()]*\))/g);
551
615
 
552
616
  for (let j = 0; j < selectors.length; j++) {
617
+ if (!selectors[j]) continue;
618
+
619
+ let queriedElement = mainElement;
620
+
553
621
  if (selectors[j].includes("@")) {
554
622
  selectors[j] = checkMediaQueries(selectors[j]);
555
623
  if (selectors[j] === false) continue;
556
624
  }
557
625
 
558
- let queriedElement = element;
559
- let specialSelectors = selectors[j].split(";");
560
- for (let k = 0; k < specialSelectors.length; k++) {
561
- // TODO: Support an array of queried elements and branch off to return matches for each
562
- // if (!Array.isArray(queriedElement)) {
563
- // queriedElement = [queriedElement]
564
- // }
565
-
566
- if (!specialSelectors[k]) continue;
567
- if (k === 0) {
568
- if (type[i] === "parent")
569
- queriedElement = queriedElement.parentElement;
570
- else if (type[i] === "next")
571
- queriedElement =
572
- queriedElement.nextElementSibling;
573
- else if (type[i] === "previous")
574
- queriedElement =
575
- queriedElement.previousElementSibling;
576
- } else if (queriedElement.contentDocument) {
577
- queriedElement = queriedElement.contentDocument;
578
- }
626
+ if (type[i] === "closest") {
627
+ let [closestSelector, remainingSelector = ""] =
628
+ selectors[j].split(/\s+/, 2);
629
+ queriedElement =
630
+ queriedElement.closest(closestSelector);
579
631
 
580
- switch (
581
- (specialSelectors[k] = specialSelectors[k].trim())
582
- ) {
583
- case "top":
584
- queriedElement = window.top.document;
585
- break;
586
- case "frame":
587
- if (queriedElement.nodeType === 9)
588
- queriedElement =
589
- queriedElement.window.frameElement;
590
- else if (queriedElement.contentDocument)
591
- queriedElement =
592
- queriedElement.contentDocument;
593
- break;
594
- case "document":
595
- queriedElement = document;
596
- break;
597
- case "parent":
598
- queriedElement = queriedElement.parentElement;
599
- break;
600
- case "next":
601
- queriedElement =
602
- queriedElement.nextElementSibling;
603
- break;
604
- case "previous":
605
- queriedElement =
606
- queriedElement.previousElementSibling;
607
- break;
608
- default:
609
- if (k === 0 && type[i] === "closest")
610
- if (specialSelectors[k].includes(" ")) {
611
- let [firstSelector, ...restSelectors] =
612
- specialSelectors[k].split(/ (.+)/);
613
- queriedElement =
614
- queriedElement.closest(
615
- firstSelector
616
- );
617
- if (restSelectors.length > 0) {
618
- if (restSelectors[0].endsWith("[]"))
619
- queriedElement =
620
- queriedElement.querySelectorAll(
621
- restSelectors[0].slice(
622
- 0,
623
- -2
624
- )
625
- );
626
- else
627
- queriedElement =
628
- queriedElement.querySelector(
629
- restSelectors[0]
630
- );
631
- }
632
- } else {
633
- // If no space, just use the selector with closest
634
- queriedElement = queriedElement.closest(
635
- specialSelectors[k]
636
- );
637
- }
638
- else if (
639
- specialSelectors[k] === "$clickedElement"
640
- ) {
641
- queriedElement =
642
- queriedElement.clickedElement;
643
- } else if (specialSelectors[k].endsWith("[]"))
644
- queriedElement =
645
- queriedElement.querySelectorAll(
646
- specialSelectors[k].slice(0, -2)
647
- );
648
- else
649
- queriedElement =
650
- queriedElement.querySelector(
651
- specialSelectors[k]
652
- );
632
+ if (!queriedElement) continue;
633
+
634
+ selectors[j] = remainingSelector;
635
+ }
636
+
637
+ let remainingSelector = selectors[j].trim();
638
+ let match;
639
+
640
+ while (
641
+ (match = queryTypesRegex.exec(remainingSelector)) !==
642
+ null
643
+ ) {
644
+ const matchIndex = match.index;
645
+ const operator = match[0];
646
+
647
+ // Process the part before the operator (if any)
648
+ const part = remainingSelector
649
+ .substring(0, matchIndex)
650
+ .trim()
651
+ .replace(/,$/, "");
652
+ if (part) {
653
+ queriedElement = querySelector(
654
+ queriedElement,
655
+ part
656
+ );
657
+ if (!queriedElement) break;
653
658
  }
659
+
660
+ // Process the operator
661
+ queriedElement = queryType(
662
+ queriedElement,
663
+ operator.substring(1)
664
+ );
654
665
  if (!queriedElement) break;
666
+
667
+ // Remove the processed part and operator from the remaining selector
668
+ remainingSelector = remainingSelector
669
+ .substring(matchIndex + operator.length)
670
+ .trim();
671
+ }
672
+
673
+ // Process the remaining part after the last operator (if any)
674
+ if (remainingSelector) {
675
+ queriedElement = querySelector(
676
+ queriedElement,
677
+ remainingSelector.trim().replace(/,$/, "")
678
+ );
655
679
  }
656
680
 
657
681
  if (
@@ -659,27 +683,66 @@
659
683
  queriedElement instanceof HTMLCollection ||
660
684
  queriedElement instanceof NodeList
661
685
  ) {
662
- for (let el of queriedElement) elements.set(el, "");
663
- } else if (queriedElement) {
664
- elements.set(queriedElement, "");
686
+ for (let el of queriedElement) {
687
+ if (el instanceof Element) {
688
+ elements.add(el);
689
+ }
690
+ }
691
+ } else if (queriedElement instanceof Element) {
692
+ elements.add(queriedElement);
665
693
  }
666
694
  }
667
- } else if (Selector === "") {
668
- if (type[i] === "parent")
669
- elements.set(element.parentElement, "");
670
- else if (type[i] === "next")
671
- elements.set(element.nextElementSibling, "");
672
- else if (type[i] === "previous")
673
- elements.set(element.previousElementSibling, "");
674
695
  }
675
696
  }
676
697
 
677
- if (!hasAttribute && !selector) elements = false;
678
- else elements = Array.from(elements.keys());
698
+ if (!hasAttribute && !selector) {
699
+ elements = false;
700
+ } else {
701
+ elements = Array.from(elements);
702
+ }
679
703
 
680
704
  return elements;
681
705
  }
682
706
 
707
+ function queryType(element, type) {
708
+ if (!element) return null;
709
+
710
+ switch (type) {
711
+ case "top":
712
+ return window.top.document;
713
+ case "frame":
714
+ // ✅ If element is a document, return the iframe element containing it
715
+ if (element.nodeType === 9) return window.frameElement;
716
+ // ✅ If element is an iframe, return it as is
717
+ return element;
718
+ case "document":
719
+ // ✅ If element is a document, return itself, else return `ownerDocument`
720
+ return element.nodeType === 9 ? element : element.ownerDocument;
721
+ case "parent":
722
+ // ✅ If it's a document, return the parent document (if inside an iframe)
723
+ if (element.nodeType === 9) {
724
+ return element.defaultView !== window.top
725
+ ? element.defaultView.parent.document
726
+ : null;
727
+ }
728
+ // ✅ Otherwise, return parent element
729
+ return element.parentElement;
730
+ case "next":
731
+ return element.nextElementSibling;
732
+ case "previous":
733
+ return element.previousElementSibling;
734
+ default:
735
+ return null;
736
+ }
737
+ }
738
+
739
+ function querySelector(element, selector) {
740
+ if (!element) return null;
741
+ return selector.endsWith("[]")
742
+ ? element.querySelectorAll(selector.slice(0, -2))
743
+ : element.querySelector(selector);
744
+ }
745
+
683
746
  const mediaRanges = {
684
747
  xs: [0, 575],
685
748
  sm: [576, 768],