hyperbook 0.72.1 → 0.73.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.
@@ -4,34 +4,39 @@ var hyperbook = (function () {
4
4
  * @param {HTMLElement} root - The root element to initialize.
5
5
  */
6
6
  const initCollapsibles = (root) => {
7
- const collapsibleEls = root.getElementsByClassName("collapsible");
8
- for (let collapsible of collapsibleEls) {
9
- const id = collapsible.parentElement.getAttribute("data-id");
10
-
11
- if (id.startsWith("_nav:") && !collapsible.classList.contains("empty")) {
12
- const link = collapsible.querySelector("a");
7
+ // Handle both navigation sections and directive-collapsible elements
8
+ const detailsEls = root.querySelectorAll("details.section, details.directive-collapsible");
9
+ for (let details of detailsEls) {
10
+ const id = details.getAttribute("data-id");
11
+
12
+ // Prevent link clicks from toggling the details element in navigation
13
+ if (id && id.startsWith("_nav:") && !details.classList.contains("empty")) {
14
+ const link = details.querySelector("summary a");
13
15
  link?.addEventListener("click", (event) => {
14
16
  event.stopPropagation();
15
17
  });
16
18
  }
17
- collapsible.addEventListener("click", () => {
18
- collapsible.classList.toggle("expanded");
19
+
20
+ // Listen for toggle events to persist state and sync with other elements
21
+ details.addEventListener("toggle", () => {
19
22
  if (id) {
20
- store.collapsibles.get(id).then((result) => {
21
- if (!result) {
22
- store.collapsibles.put({ id }).then(() => {
23
- updateCollapsibles(root);
24
- });
25
- } else {
26
- store.collapsibles.delete(id).then(() => {
27
- updateCollapsibles(root);
28
- });
23
+ if (details.open) {
24
+ store.collapsibles.put({ id });
25
+ } else {
26
+ store.collapsibles.delete(id);
27
+ }
28
+
29
+ // Sync all elements with the same ID
30
+ const allWithSameId = document.querySelectorAll(`[data-id="${id}"]`);
31
+ for (let el of allWithSameId) {
32
+ if (el !== details && el.tagName === "DETAILS") {
33
+ el.open = details.open;
29
34
  }
30
- });
35
+ }
31
36
  }
32
37
 
33
38
  setTimeout(() => {
34
- window.dispatchEvent(new Event("resize")); // geogebra new this in order resize the applet
39
+ window.dispatchEvent(new Event("resize")); // geogebra needs this to resize the applet
35
40
  }, 100);
36
41
  });
37
42
  }
@@ -43,15 +48,15 @@ var hyperbook = (function () {
43
48
  */
44
49
  const updateCollapsibles = (root) => {
45
50
  store.collapsibles.toArray().then((collapsibles) => {
46
- const collapsibleEls = root.getElementsByClassName("collapsible");
47
- for (let collapsibleEl of collapsibleEls) {
48
- const id = collapsibleEl.parentElement.getAttribute("data-id");
51
+ const detailsEls = root.querySelectorAll("details.section, details.directive-collapsible");
52
+ for (let details of detailsEls) {
53
+ const id = details.getAttribute("data-id");
49
54
  if (id) {
50
- const expanded = collapsibles.some((c) => c.id === id);
51
- if (expanded) {
52
- collapsibleEl.classList.add("expanded");
53
- } else if (!id.startsWith("_nav:")) {
54
- collapsibleEl.classList.remove("expanded");
55
+ const shouldBeOpen = collapsibles.some((c) => c.id === id);
56
+ // Only update if state differs and it's not a navigation section
57
+ // (navigation sections are auto-expanded based on current page)
58
+ if (!id.startsWith("_nav:") && details.open !== shouldBeOpen) {
59
+ details.open = shouldBeOpen;
55
60
  }
56
61
  }
57
62
  }
@@ -2,7 +2,7 @@
2
2
  display: flow-root;
3
3
  }
4
4
 
5
- .directive-collapsible>button {
5
+ .directive-collapsible>summary {
6
6
  width: 100%;
7
7
  text-align: left;
8
8
  border-style: solid;
@@ -13,37 +13,46 @@
13
13
  padding: 8px 16px;
14
14
  border-radius: 4px;
15
15
  margin-bottom: 10px;
16
+ list-style: none;
17
+ color: var(--color-text);
18
+ border-color: var(--color-spacer);
19
+ background-color: var(--color-nav);
20
+ }
21
+
22
+ .directive-collapsible>summary::-webkit-details-marker {
23
+ display: none;
16
24
  }
17
25
 
18
- .directive-collapsible>button.expanded {
26
+ .directive-collapsible>summary::marker {
27
+ display: none;
28
+ }
29
+
30
+ .directive-collapsible[open]>summary {
19
31
  border-bottom-left-radius: 0px;
20
32
  border-bottom-right-radius: 0px;
21
33
  margin-bottom: 0px;
22
34
  }
23
35
 
24
- .directive-collapsible>button:after {
36
+ .directive-collapsible>summary:after {
25
37
  content: "\02795";
26
38
  /* Unicode character for "plus" sign (+) */
27
39
  font-size: 13px;
28
40
  float: right;
29
41
  margin-left: 5px;
42
+ color: var(--color-text);
30
43
  }
31
44
 
32
- .directive-collapsible>button.expanded:after {
45
+ .directive-collapsible[open]>summary:after {
33
46
  content: "\2796";
34
47
  /* Unicode character for "minus" sign (-) */
35
48
  }
36
49
 
37
- .directive-collapsible .collapsible-content {
50
+ .directive-collapsible .content {
38
51
  border-top: none;
39
52
  margin-bottom: 10px;
40
53
  }
41
54
 
42
- .directive-collapsible .collapsible-content {
43
- display: none;
44
- }
45
-
46
- .directive-collapsible>button.expanded+.collapsible-content {
55
+ .directive-collapsible[open] .content {
47
56
  display: block;
48
57
  border-style: solid;
49
58
  border-width: 1px;
@@ -53,18 +62,5 @@
53
62
  margin-bottom: 10px;
54
63
  border-bottom-left-radius: 4px;
55
64
  border-bottom-right-radius: 4px;
56
- }
57
-
58
- .directive-collapsible>button {
59
- color: var(--color-text);
60
- border-color: var(--color-spacer);
61
- background-color: var(--color-nav);
62
- }
63
-
64
- .directive-collapsible>button:after {
65
- color: var(--color-text);
66
- }
67
-
68
- .directive-collapsible .collapsible-content {
69
65
  border-color: var(--color-spacer);
70
66
  }
@@ -1,43 +1,47 @@
1
1
  hyperbook.tabs = (function () {
2
2
  const init = (root) => {
3
- let allTabs = root.querySelectorAll(".directive-tabs .tab[data-tabs-id]");
4
- allTabs.forEach((tab) => {
5
- const tabsId = tab.getAttribute("data-tabs-id");
6
- const tabId = tab.getAttribute("data-tab-id");
7
- tab.addEventListener("click", () => {
8
- store.tabs.put({
9
- id: tabsId,
10
- active: tabId,
11
- });
12
- selectTab(tabsId, tabId);
3
+ // Find all radio inputs for tabs
4
+ let allTabInputs = root.querySelectorAll(".directive-tabs .tab-input[data-tabs-id]");
5
+ allTabInputs.forEach((input) => {
6
+ const tabsId = input.getAttribute("data-tabs-id");
7
+ const tabId = input.getAttribute("data-tab-id");
8
+
9
+ // Listen for changes on radio inputs
10
+ input.addEventListener("change", () => {
11
+ if (input.checked) {
12
+ store.tabs.put({
13
+ id: tabsId,
14
+ active: tabId,
15
+ });
16
+
17
+ // Sync all radio inputs with the same tabs-id and tab-id
18
+ syncAllTabs(tabsId, tabId);
19
+ }
13
20
  });
14
21
  });
22
+
23
+ // Restore saved tab selections
15
24
  store.tabs.each((result) => {
16
25
  selectTab(result.id, result.active);
17
26
  });
18
27
  };
19
28
 
20
- function selectTab(tabsId, tabId) {
21
- let relevantTabButtons = document.querySelectorAll(
22
- `.directive-tabs .tab[data-tabs-id="${tabsId}"]`
23
- );
24
- relevantTabButtons.forEach((e) => {
25
- e.className = e.className.replace(" active", "");
26
- if (e.getAttribute("data-tab-id") == tabId) {
27
- e.className += " active";
28
- }
29
- });
30
- let relevantTabPanels = document.querySelectorAll(
31
- `.directive-tabs .tabpanel[data-tabs-id="${tabsId}"]`
29
+ function syncAllTabs(tabsId, tabId) {
30
+ // Find all radio inputs with the same tabs-id and tab-id across the page
31
+ const allMatchingInputs = document.querySelectorAll(
32
+ `.directive-tabs .tab-input[data-tabs-id="${tabsId}"][data-tab-id="${tabId}"]`
32
33
  );
33
- relevantTabPanels.forEach((e) => {
34
- e.className = e.className.replace(" active", "");
35
- if (e.getAttribute("data-tab-id") == tabId) {
36
- e.className += " active";
37
- }
34
+
35
+ allMatchingInputs.forEach((input) => {
36
+ input.checked = true;
38
37
  });
39
38
  }
40
39
 
40
+ function selectTab(tabsId, tabId) {
41
+ // Sync all tabs with this combination
42
+ syncAllTabs(tabsId, tabId);
43
+ }
44
+
41
45
  init(document.body);
42
46
 
43
47
  // Observe for new tabs added to the DOM
@@ -10,6 +10,15 @@
10
10
  border-width: 1px;
11
11
  border-top-right-radius: 4px;
12
12
  border-top-left-radius: 4px;
13
+ background-color: var(--color-nav);
14
+ border-color: var(--color-spacer);
15
+ }
16
+
17
+ /* Hide radio inputs */
18
+ .directive-tabs .tab-input {
19
+ position: absolute;
20
+ opacity: 0;
21
+ pointer-events: none;
13
22
  }
14
23
 
15
24
  .directive-tabs > .tabs .tab {
@@ -23,6 +32,7 @@
23
32
  padding: 8px 16px;
24
33
  transition: 0.3s;
25
34
  border-bottom: 4px solid transparent;
35
+ color: var(--color-text);
26
36
  }
27
37
 
28
38
  .directive-tabs > .tabpanel {
@@ -34,33 +44,106 @@
34
44
  margin-bottom: 12px;
35
45
  border-bottom-right-radius: 4px;
36
46
  border-bottom-left-radius: 4px;
47
+ border-color: var(--color-spacer);
48
+ display: none;
37
49
  }
38
50
 
39
- .directive-tabs .tabs {
40
- background-color: var(--color-nav);
41
- border-color: var(--color-spacer);
51
+ .directive-tabs .tabs .tab:hover {
52
+ border-bottom-color: var(--color-spacer);
53
+ color: var(--color-brand);
42
54
  }
43
55
 
44
- .directive-tabs .tab {
45
- color: var(--color-text);
56
+ /* Style active tab based on radio button state using :has() */
57
+ /* Check each input position and style the corresponding label */
58
+ .directive-tabs:has(> .tab-input:nth-of-type(1):checked) .tabs .tab:nth-of-type(1) {
59
+ border-bottom-color: var(--color-brand);
60
+ color: var(--color-brand);
46
61
  }
47
62
 
48
- .directive-tabs .tabs .tab:hover {
49
- border-bottom-color: var(--color-spacer);
63
+ .directive-tabs:has(> .tab-input:nth-of-type(2):checked) .tabs .tab:nth-of-type(2) {
64
+ border-bottom-color: var(--color-brand);
50
65
  color: var(--color-brand);
51
66
  }
52
67
 
53
- .directive-tabs .tabs .tab.active {
68
+ .directive-tabs:has(> .tab-input:nth-of-type(3):checked) .tabs .tab:nth-of-type(3) {
54
69
  border-bottom-color: var(--color-brand);
55
70
  color: var(--color-brand);
56
71
  }
57
72
 
58
- .directive-tabs .tabpanel {
59
- border-color: var(--color-spacer);
60
- display: none;
73
+ .directive-tabs:has(> .tab-input:nth-of-type(4):checked) .tabs .tab:nth-of-type(4) {
74
+ border-bottom-color: var(--color-brand);
75
+ color: var(--color-brand);
76
+ }
77
+
78
+ .directive-tabs:has(> .tab-input:nth-of-type(5):checked) .tabs .tab:nth-of-type(5) {
79
+ border-bottom-color: var(--color-brand);
80
+ color: var(--color-brand);
81
+ }
82
+
83
+ .directive-tabs:has(> .tab-input:nth-of-type(6):checked) .tabs .tab:nth-of-type(6) {
84
+ border-bottom-color: var(--color-brand);
85
+ color: var(--color-brand);
86
+ }
87
+
88
+ .directive-tabs:has(> .tab-input:nth-of-type(7):checked) .tabs .tab:nth-of-type(7) {
89
+ border-bottom-color: var(--color-brand);
90
+ color: var(--color-brand);
91
+ }
92
+
93
+ .directive-tabs:has(> .tab-input:nth-of-type(8):checked) .tabs .tab:nth-of-type(8) {
94
+ border-bottom-color: var(--color-brand);
95
+ color: var(--color-brand);
96
+ }
97
+
98
+ .directive-tabs:has(> .tab-input:nth-of-type(9):checked) .tabs .tab:nth-of-type(9) {
99
+ border-bottom-color: var(--color-brand);
100
+ color: var(--color-brand);
101
+ }
102
+
103
+ .directive-tabs:has(> .tab-input:nth-of-type(10):checked) .tabs .tab:nth-of-type(10) {
104
+ border-bottom-color: var(--color-brand);
105
+ color: var(--color-brand);
106
+ }
107
+
108
+ /* Show tabpanel based on checked radio input using general sibling combinator */
109
+ /* Note: nth-of-type counts by element type, and .tabs div is the first div */
110
+ .directive-tabs > .tab-input:nth-of-type(1):checked ~ .tabpanel:nth-of-type(2) {
111
+ display: block;
112
+ }
113
+
114
+ .directive-tabs > .tab-input:nth-of-type(2):checked ~ .tabpanel:nth-of-type(3) {
115
+ display: block;
116
+ }
117
+
118
+ .directive-tabs > .tab-input:nth-of-type(3):checked ~ .tabpanel:nth-of-type(4) {
119
+ display: block;
120
+ }
121
+
122
+ .directive-tabs > .tab-input:nth-of-type(4):checked ~ .tabpanel:nth-of-type(5) {
123
+ display: block;
124
+ }
125
+
126
+ .directive-tabs > .tab-input:nth-of-type(5):checked ~ .tabpanel:nth-of-type(6) {
127
+ display: block;
128
+ }
129
+
130
+ .directive-tabs > .tab-input:nth-of-type(6):checked ~ .tabpanel:nth-of-type(7) {
131
+ display: block;
132
+ }
133
+
134
+ .directive-tabs > .tab-input:nth-of-type(7):checked ~ .tabpanel:nth-of-type(8) {
135
+ display: block;
136
+ }
137
+
138
+ .directive-tabs > .tab-input:nth-of-type(8):checked ~ .tabpanel:nth-of-type(9) {
139
+ display: block;
140
+ }
141
+
142
+ .directive-tabs > .tab-input:nth-of-type(9):checked ~ .tabpanel:nth-of-type(10) {
143
+ display: block;
61
144
  }
62
145
 
63
- .directive-tabs .tabpanel.active {
146
+ .directive-tabs > .tab-input:nth-of-type(10):checked ~ .tabpanel:nth-of-type(11) {
64
147
  display: block;
65
148
  }
66
149