panelset 0.5.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 (39) hide show
  1. package/dist/index.d.ts +111 -0
  2. package/dist/panelset.css +1 -0
  3. package/dist/panelset.js +317 -0
  4. package/dist/panelset.js.map +1 -0
  5. package/package.json +49 -0
  6. package/src/docs/assets/scripts/copybutton.js +44 -0
  7. package/src/docs/assets/scripts/example-async.js +161 -0
  8. package/src/docs/assets/scripts/example-closable.js +27 -0
  9. package/src/docs/assets/scripts/example-megamenu.js +84 -0
  10. package/src/docs/assets/scripts/example.js +29 -0
  11. package/src/docs/assets/scripts/main.js +7 -0
  12. package/src/docs/assets/styles/_base.scss +13 -0
  13. package/src/docs/assets/styles/_code.scss +121 -0
  14. package/src/docs/assets/styles/_demos.scss +180 -0
  15. package/src/docs/assets/styles/_landingpage.scss +41 -0
  16. package/src/docs/assets/styles/_layout.scss +80 -0
  17. package/src/docs/assets/styles/_sidebar.scss +67 -0
  18. package/src/docs/assets/styles/_typography.scss +116 -0
  19. package/src/docs/assets/styles/_variables.scss +32 -0
  20. package/src/docs/assets/styles/docs.scss +64 -0
  21. package/src/docs/views/api-reference.pug +474 -0
  22. package/src/docs/views/configuration.pug +173 -0
  23. package/src/docs/views/events.pug +222 -0
  24. package/src/docs/views/examples/async.pug +268 -0
  25. package/src/docs/views/examples/basic.pug +155 -0
  26. package/src/docs/views/examples/closable.pug +97 -0
  27. package/src/docs/views/getting-started.pug +99 -0
  28. package/src/docs/views/index.pug +38 -0
  29. package/src/docs/views/templates/includes/_head.pug +11 -0
  30. package/src/docs/views/templates/includes/_mixins.pug +100 -0
  31. package/src/docs/views/templates/includes/_scripts.pug +14 -0
  32. package/src/docs/views/templates/includes/_sidebar.pug +18 -0
  33. package/src/docs/views/templates/layouts/_base.pug +36 -0
  34. package/src/docs/views/transitions.pug +141 -0
  35. package/src/lib/index.ts +685 -0
  36. package/src/lib/styles/_base.scss +99 -0
  37. package/src/lib/styles/_loading.scss +47 -0
  38. package/src/lib/styles/_variables.scss +19 -0
  39. package/src/lib/styles/panelset.scss +3 -0
@@ -0,0 +1,111 @@
1
+ export declare interface ActivationAbortedEventDetail {
2
+ panelId: string;
3
+ trigger: string | null;
4
+ }
5
+
6
+ export declare interface ActivationEventDetail {
7
+ panelId: string;
8
+ trigger: string | null;
9
+ }
10
+
11
+ export declare type AsyncContentHandler = (targetPanel: HTMLElement, signal: AbortSignal) => Promise<void> | void;
12
+
13
+ export declare interface BeforeActivateEventDetail {
14
+ panelId: string;
15
+ targetPanel: HTMLElement;
16
+ outgoingPanel: HTMLElement | null;
17
+ signal: AbortSignal;
18
+ promise: Promise<void> | null;
19
+ }
20
+
21
+ export declare interface HandlerOptions {
22
+ once?: boolean;
23
+ }
24
+
25
+ export declare class PanelSet {
26
+ static defaults: Required<Omit<PanelSetConfig, 'selector'>>;
27
+ element: HTMLElement;
28
+ config: Required<Omit<PanelSetConfig, 'selector'>>;
29
+ panels: HTMLElement[];
30
+ activePanel: HTMLElement;
31
+ panelWrapper: HTMLElement;
32
+ pendingPanel: HTMLElement;
33
+ private _openCloseGeneration;
34
+ private _isLoadingAsync;
35
+ private _currentAbortController?;
36
+ static _getDataConfig(element: HTMLElement): Partial<PanelSetConfig>;
37
+ static _mergeConfig(defaults: Required<Omit<PanelSetConfig, 'selector'>>, dataConfig: Partial<PanelSetConfig>, options: Partial<PanelSetConfig>): Required<Omit<PanelSetConfig, 'selector'>>;
38
+ /**
39
+ * Initialize PanelSet instances
40
+ * @param selectorOrOptions - CSS selector string or config object
41
+ * @param options - Additional config options (when first param is selector)
42
+ * @returns Array of PanelSet instances
43
+ */
44
+ static init(selectorOrOptions?: string | PanelSetConfig, options?: PanelSetConfig): PanelSet[];
45
+ constructor(elementOrSelector: HTMLElement | string, options?: PanelSetConfig);
46
+ private _log;
47
+ private _autoWrapPanels;
48
+ private _internalInit;
49
+ private _dispatch;
50
+ private _getVerticalMetrics;
51
+ private _measureHeight;
52
+ private _cleanupPanels;
53
+ private _waitForTransition;
54
+ private _animateOpenClose;
55
+ /**
56
+ * Get the ID of the currently active panel
57
+ * @returns Panel ID or null if no panel is active
58
+ */
59
+ getActive(): string | null;
60
+ /**
61
+ * Open a closable panelset
62
+ * @param withTransition - Whether to animate
63
+ */
64
+ open(withTransition?: boolean): void;
65
+ /**
66
+ * Close a closable panelset
67
+ * @param withTransition - Whether to animate
68
+ */
69
+ close(withTransition?: boolean): void;
70
+ /**
71
+ * Toggle a closable panelset between open and closed
72
+ * @param withTransition - Whether to animate
73
+ */
74
+ toggle(withTransition?: boolean): void;
75
+ /**
76
+ * Register a handler for async content loading
77
+ * @param handler - Async content handler function
78
+ * @param options - Handler options (once: whether to load only once)
79
+ */
80
+ onBeforeActivate(handler: AsyncContentHandler, options?: HandlerOptions): void;
81
+ /**
82
+ * Show a panel by ID
83
+ * @param panelId - ID of the panel to show
84
+ * @param withTransition - Whether to animate the transition
85
+ * @param options - Additional options (trigger name)
86
+ */
87
+ show(panelId: string, withTransition?: boolean, options?: ShowOptions): Promise<void>;
88
+ }
89
+
90
+ export declare interface PanelSetConfig {
91
+ transitions?: boolean | {
92
+ panels?: boolean;
93
+ height?: boolean;
94
+ };
95
+ closable?: boolean;
96
+ emptyPanelHeight?: number;
97
+ loadingDelay?: number;
98
+ debug?: boolean;
99
+ selector?: string;
100
+ }
101
+
102
+ export declare interface ReadyEventDetail {
103
+ container: HTMLElement;
104
+ instance: PanelSet;
105
+ }
106
+
107
+ export declare interface ShowOptions {
108
+ trigger?: string;
109
+ }
110
+
111
+ export { }
@@ -0,0 +1 @@
1
+ [data-panelset]{--fadeout-speed: .5s;--fadein-speed: .5s;--fadein-delay: .5s;--height-duration-ratio: 1;--transition-timing: ease-in-out;--loading-panel-opacity: .1;--close-speed: .5s;--open-speed: .5s;--open-timing: ease-in-out;--close-timing: ease-in-out;--loading-dim-duration: calc(var(--fadeout-speed) * (1 - var(--loading-panel-opacity)));--loading-fadeout-duration: calc(var(--fadeout-speed) * var(--loading-panel-opacity))}[data-panelset]{position:relative;transition:height calc((var(--fadein-speed) + var(--fadein-delay)) * var(--height-duration-ratio)) var(--transition-timing);will-change:height;box-sizing:border-box}[data-panelset] .panel-wrapper{position:relative}[data-panelset].is-closed{height:0!important;min-height:0}[data-panelset].is-closed .panel-wrapper{opacity:0;pointer-events:none}[data-panelset].is-opening{transition:height var(--open-speed) var(--open-timing),box-shadow var(--open-speed) var(--open-timing),border-color var(--open-speed) var(--open-timing)}[data-panelset].is-opening .panel-wrapper{transition:opacity var(--open-speed) var(--open-timing);transition-delay:calc(.5 * var(--open-speed));opacity:1}[data-panelset].is-closing{transition:height var(--close-speed) var(--close-timing),box-shadow var(--close-speed) var(--close-timing),border-color var(--open-speed) var(--open-timing)}[data-panelset].is-closing .panel-wrapper{transition:opacity var(--close-speed) var(--close-timing);opacity:0}[data-panelset] [role=tabpanel]{opacity:0;position:absolute;top:0;left:0;width:100%;box-sizing:border-box}[data-panelset] [role=tabpanel].fade{transition:all var(--fadeout-speed) var(--transition-timing);pointer-events:none}[data-panelset] [role=tabpanel].fade.incoming{transition-duration:var(--fadein-speed);transition-delay:var(--fadein-delay)}[data-panelset] [role=tabpanel].active{opacity:1;position:relative}[data-panelset] [role=tabpanel].no-transition,[data-panelset] [role=tabpanel].incoming.no-transition{transition:none}[data-panelset].is-transitioning,[data-panelset].is-opening,[data-panelset].is-closing{overflow:hidden}@media(prefers-reduced-motion:reduce){[data-panelset]{--fadeout-speed: 0s !important;--fadein-speed: 0s !important;--fadein-delay: 0s !important;--loading-dim-duration: 0s !important;--loading-fadeout-duration: 0s !important;--close-speed: 0s !important;--open-speed: 0s !important;transition:none!important}[data-panelset] [role=tabpanel]{transition:none!important}[data-panelset]:after{transition:none!important;animation-play-state:paused!important}[data-panelset] .panel-wrapper{transition:none!important}}[data-panelset]:after{content:"";position:absolute;top:50%;left:50%;width:40px;height:40px;transform:translate(-50%,-50%);border:4px solid rgba(0,0,0,.1);border-top-color:#0064ffcc;border-radius:50%;opacity:0;pointer-events:none;transition:opacity var(--loading-dim-duration) var(--transition-timing);animation:spin .8s linear infinite;animation-play-state:running;z-index:11}[data-panelset] .panel-wrapper{transition:opacity var(--fadeout-speed) var(--transition-timing) var(--fadein-speed)}[data-panelset].is-loading{pointer-events:none}[data-panelset].is-loading:after{opacity:1}[data-panelset].is-loading .panel-wrapper{transition:opacity var(--fadeout-speed) var(--transition-timing);opacity:var(--loading-panel-opacity)}@keyframes spin{to{transform:translate(-50%,-50%) rotate(360deg)}}
@@ -0,0 +1,317 @@
1
+ const d = class d {
2
+ constructor(e, t = {}) {
3
+ this._openCloseGeneration = 0, this._isLoadingAsync = !1;
4
+ let s;
5
+ if (typeof e == "string") {
6
+ if (s = document.querySelector(e), !s)
7
+ throw new Error(`PanelSet: No element found for selector "${e}"`);
8
+ } else
9
+ s = e;
10
+ if (this.element = s, s.panelSet || s.dataset.panelset === "true")
11
+ return console.warn("PanelSet: Element already initialized, returning existing instance"), s.panelSet;
12
+ s.panelSet = this;
13
+ const i = d._getDataConfig(s);
14
+ this.config = d._mergeConfig(
15
+ d.defaults,
16
+ i,
17
+ t
18
+ ), this.panels = Array.from(s.querySelectorAll('[role="tabpanel"]')), this.activePanel = this.panels.find((n) => n.classList.contains("active")) || this.panels[0], this.panelWrapper = this.element.querySelector(".panel-wrapper") || this._autoWrapPanels(), this.pendingPanel = this.activePanel, this._internalInit(), this.element.dataset.panelset = "true", this._log(`Initialized (${this.panels.length} panels)`), this._dispatch("ps:ready", { container: this.element, instance: this });
19
+ }
20
+ // Parse data attributes from element
21
+ static _getDataConfig(e) {
22
+ const t = e.dataset, s = {}, i = {
23
+ transitions: "json",
24
+ closable: "boolean",
25
+ emptyPanelHeight: "number",
26
+ loadingDelay: "number",
27
+ debug: "boolean"
28
+ };
29
+ for (const [n, a] of Object.entries(i)) {
30
+ if (!(n in t)) continue;
31
+ const o = t[n];
32
+ if (o !== void 0)
33
+ switch (a) {
34
+ case "boolean":
35
+ s[n] = o !== "false";
36
+ break;
37
+ case "number":
38
+ s[n] = parseInt(o, 10);
39
+ break;
40
+ case "json":
41
+ try {
42
+ s[n] = JSON.parse(o);
43
+ } catch {
44
+ s[n] = o !== "false";
45
+ }
46
+ break;
47
+ }
48
+ }
49
+ return s;
50
+ }
51
+ // Merge configurations
52
+ static _mergeConfig(e, t, s) {
53
+ return {
54
+ ...e,
55
+ ...t,
56
+ ...s
57
+ };
58
+ }
59
+ /**
60
+ * Initialize PanelSet instances
61
+ * @param selectorOrOptions - CSS selector string or config object
62
+ * @param options - Additional config options (when first param is selector)
63
+ * @returns Array of PanelSet instances
64
+ */
65
+ static init(e = {}, t = {}) {
66
+ let s, i;
67
+ typeof e == "string" ? (s = e, i = t) : (i = e, s = i.selector || "[data-panelset]");
68
+ const n = document.querySelectorAll(s), a = [];
69
+ return n.forEach((o) => {
70
+ if (o.panelSet || o.dataset.panelset === "true") {
71
+ o.panelSet && a.push(o.panelSet);
72
+ return;
73
+ }
74
+ const l = new d(o, i);
75
+ a.push(l);
76
+ }), a;
77
+ }
78
+ // Debug logging helper
79
+ _log(e) {
80
+ if (!this.config.debug) return;
81
+ const t = this.element.id || "no id";
82
+ console.log(`[PanelSet] - "${t}" -`, e);
83
+ }
84
+ _autoWrapPanels() {
85
+ const e = document.createElement("div");
86
+ return e.className = "panel-wrapper", this.panels.forEach((t) => e.appendChild(t)), this.element.appendChild(e), e;
87
+ }
88
+ _internalInit() {
89
+ this.panels.forEach((e) => {
90
+ e.classList.remove("fade", "incoming"), e !== this.activePanel ? (e.hidden = !0, e.classList.remove("active")) : (e.hidden = !1, e.classList.add("active"));
91
+ }), this.element.style.height = "";
92
+ }
93
+ // Dispatch custom event helper
94
+ _dispatch(e, t) {
95
+ this.element.dispatchEvent(
96
+ new CustomEvent(e, {
97
+ detail: t,
98
+ bubbles: !0,
99
+ cancelable: !1
100
+ })
101
+ );
102
+ }
103
+ /* --- Modular helpers --- */
104
+ _getVerticalMetrics(e) {
105
+ if (!e) return 0;
106
+ const t = getComputedStyle(e);
107
+ return ["paddingTop", "paddingBottom", "borderTopWidth", "borderBottomWidth"].reduce((s, i) => s + (parseFloat(t[i]) || 0), 0);
108
+ }
109
+ _measureHeight(e) {
110
+ let t = e.offsetHeight;
111
+ return t += this._getVerticalMetrics(this.panelWrapper), t += this._getVerticalMetrics(this.element), t;
112
+ }
113
+ _cleanupPanels(e) {
114
+ this.panels.forEach((t) => {
115
+ t.classList.remove("fade", "incoming"), t !== e ? (t.classList.remove("active"), t.hidden = !0) : (t.classList.add("active"), t.hidden = !1);
116
+ }), this.element.style.height = "", this.element.classList.remove("is-transitioning"), this.activePanel = e;
117
+ }
118
+ _waitForTransition(e) {
119
+ return new Promise((t) => {
120
+ const s = getComputedStyle(e), i = parseFloat(s.transitionDuration) || 0, n = parseFloat(s.transitionDelay) || 0;
121
+ if (i + n === 0) {
122
+ t();
123
+ return;
124
+ }
125
+ const a = (o) => {
126
+ o.target === e && (e.removeEventListener("transitionend", a), t());
127
+ };
128
+ e.addEventListener("transitionend", a);
129
+ });
130
+ }
131
+ // Shared helper for open/close
132
+ _animateOpenClose(e, t) {
133
+ const s = e ? "opening" : "closing", n = `is-${e ? "closing" : "opening"}`, a = `is-${s}`;
134
+ this._log(e ? "Opening" : "Closing"), this._openCloseGeneration++;
135
+ const o = this._openCloseGeneration;
136
+ this.element.classList.contains(n) && this.element.classList.remove(n);
137
+ const l = e ? this._measureHeight(this.pendingPanel) : 0;
138
+ if (t && this.config.transitions) {
139
+ this.element.classList.add(a);
140
+ const c = this.element.offsetHeight;
141
+ this.element.style.height = `${c}px`, e && this.element.classList.remove("is-closed"), requestAnimationFrame(() => {
142
+ this.element.style.height = `${l}px`, this._waitForTransition(this.element).then(() => {
143
+ this._openCloseGeneration === o && (this.element.style.height = "", this.element.classList.remove(a), e || this.element.classList.add("is-closed"));
144
+ });
145
+ });
146
+ } else
147
+ e ? this.element.classList.remove("is-closed") : this.element.classList.add("is-closed"), this.element.style.height = "";
148
+ }
149
+ /**
150
+ * Get the ID of the currently active panel
151
+ * @returns Panel ID or null if no panel is active
152
+ */
153
+ getActive() {
154
+ return this.pendingPanel?.id || null;
155
+ }
156
+ /**
157
+ * Open a closable panelset
158
+ * @param withTransition - Whether to animate
159
+ */
160
+ open(e = !0) {
161
+ if (!this.config.closable) {
162
+ this._log("Cannot open: closable is false");
163
+ return;
164
+ }
165
+ const t = this.element.classList.contains("is-closed"), s = this.element.classList.contains("is-closing");
166
+ !t && !s || this._animateOpenClose(!0, e);
167
+ }
168
+ /**
169
+ * Close a closable panelset
170
+ * @param withTransition - Whether to animate
171
+ */
172
+ close(e = !0) {
173
+ if (!this.config.closable) {
174
+ this._log("Cannot close: closable is false");
175
+ return;
176
+ }
177
+ const t = this.element.classList.contains("is-closed"), s = this.element.classList.contains("is-opening");
178
+ t && !s || this._animateOpenClose(!1, e);
179
+ }
180
+ /**
181
+ * Toggle a closable panelset between open and closed
182
+ * @param withTransition - Whether to animate
183
+ */
184
+ toggle(e = !0) {
185
+ const t = this.element.classList.contains("is-closed"), s = this.element.classList.contains("is-closing");
186
+ t || s ? this.open(e) : this.close(e);
187
+ }
188
+ /**
189
+ * Register a handler for async content loading
190
+ * @param handler - Async content handler function
191
+ * @param options - Handler options (once: whether to load only once)
192
+ */
193
+ onBeforeActivate(e, t = {}) {
194
+ const s = t.once === !0;
195
+ this.element.addEventListener("ps:beforeactivate", (i) => {
196
+ const n = i, { targetPanel: a, signal: o } = n.detail;
197
+ if (s && a.dataset.loaded === "true") {
198
+ this.config.debug && this._log(`Skipping ${a.id} (already loaded)`);
199
+ return;
200
+ }
201
+ const l = e(a, o);
202
+ l && typeof l.then == "function" && (n.detail.promise = l.then(() => {
203
+ s && (a.dataset.loaded = "true");
204
+ }).catch((c) => {
205
+ throw c.name === "AbortError" ? (this.config.debug && this._log(`Load aborted: ${a.id}`), c) : (this._log(`Load failed: ${c.message}`), c);
206
+ }));
207
+ });
208
+ }
209
+ /* --- Main logic --- */
210
+ /**
211
+ * Show a panel by ID
212
+ * @param panelId - ID of the panel to show
213
+ * @param withTransition - Whether to animate the transition
214
+ * @param options - Additional options (trigger name)
215
+ */
216
+ async show(e, t = !0, s = {}) {
217
+ const i = this.panels.find((r) => r.id === e);
218
+ if (!i) {
219
+ this._log(`Panel not found: ${e}`);
220
+ return;
221
+ }
222
+ if (i === this.pendingPanel) return;
223
+ const n = this.pendingPanel, a = n?.id;
224
+ this.pendingPanel = i, this.element.classList.remove("is-loading"), n && n !== this.activePanel && n !== i && (n.classList.remove("incoming"), n.hidden || n.classList.remove("active"));
225
+ const o = this._isLoadingAsync;
226
+ this._currentAbortController && (this._currentAbortController.abort(), o && a && a !== e && this._dispatch("ps:activationaborted", {
227
+ panelId: a,
228
+ trigger: null
229
+ }));
230
+ const l = new AbortController();
231
+ this._currentAbortController = l, this._isLoadingAsync = !1, this._log(`${n?.id || "none"} → ${e}`);
232
+ const c = {
233
+ panelId: e,
234
+ targetPanel: i,
235
+ outgoingPanel: n,
236
+ signal: l.signal,
237
+ promise: null
238
+ }, P = new CustomEvent("ps:beforeactivate", {
239
+ detail: c,
240
+ bubbles: !0,
241
+ cancelable: !1
242
+ });
243
+ this.element.dispatchEvent(P);
244
+ const _ = c.promise;
245
+ if (_) {
246
+ this._isLoadingAsync = !0, this._log("Waiting for content...");
247
+ let r, g = !1;
248
+ if (this.config.loadingDelay > 0 ? r = setTimeout(() => {
249
+ this.element.classList.add("is-loading"), g = !0;
250
+ }, this.config.loadingDelay) : (this.element.classList.add("is-loading"), g = !0), !(this.activePanel && this.activePanel !== i)) {
251
+ const m = t !== !1 && this.config.transitions !== !1;
252
+ let u = m;
253
+ if (typeof this.config.transitions == "object" && (u = m && this.config.transitions.height !== !1), u) {
254
+ const C = this.element.offsetHeight;
255
+ this.element.style.height = `${C}px`, requestAnimationFrame(() => {
256
+ this.element.style.height = `${this.config.emptyPanelHeight}px`;
257
+ });
258
+ }
259
+ }
260
+ try {
261
+ if (await _, r && clearTimeout(r), l.signal.aborted) {
262
+ this._log(`Aborted during load: ${e}`), g && this.element.classList.remove("is-loading");
263
+ return;
264
+ }
265
+ this._log("Content loaded");
266
+ } catch (m) {
267
+ r && clearTimeout(r);
268
+ const u = m;
269
+ this._log(`Load failed: ${u.message}`), g && this.element.classList.remove("is-loading"), u.name !== "AbortError" && console.error("Panel load error:", m);
270
+ return;
271
+ }
272
+ g && this.element.classList.remove("is-loading");
273
+ }
274
+ if (l.signal.aborted) {
275
+ this._log(`Aborted: ${e}`);
276
+ return;
277
+ }
278
+ this._dispatch("ps:activationstart", {
279
+ panelId: e,
280
+ trigger: s.trigger || null
281
+ });
282
+ const p = t !== !1 && this.config.transitions !== !1;
283
+ let v = p, b = p;
284
+ typeof this.config.transitions == "object" && (v = p && this.config.transitions.panels !== !1, b = p && this.config.transitions.height !== !1), this.panels.forEach((r) => r.classList.toggle("fade", v));
285
+ const L = this.element.offsetHeight;
286
+ b && (this.element.style.height = `${L}px`);
287
+ const h = this.activePanel;
288
+ i.hidden = !1, i.classList.add("incoming"), v && this.element.classList.add("is-transitioning"), h && h !== i && (h.classList.remove("active", "incoming"), h.hidden = !1), requestAnimationFrame(() => {
289
+ i.classList.add("active"), h && h !== i && h.classList.remove("incoming");
290
+ const r = this._measureHeight(i), g = L !== r;
291
+ b && (this.element.style.height = `${r}px`);
292
+ const f = [];
293
+ v && f.push(this._waitForTransition(i)), b && g && f.push(this._waitForTransition(this.element)), f.length || f.push(Promise.resolve()), Promise.all(f).then(() => {
294
+ if (this.pendingPanel !== i) {
295
+ this._log(`Interrupted: ${e}`);
296
+ return;
297
+ }
298
+ this._cleanupPanels(i), this._log(`✓ ${e}`), this._dispatch("ps:activationcomplete", {
299
+ panelId: e,
300
+ trigger: s.trigger || null
301
+ });
302
+ });
303
+ });
304
+ }
305
+ };
306
+ d.defaults = {
307
+ transitions: !0,
308
+ closable: !1,
309
+ emptyPanelHeight: 200,
310
+ loadingDelay: 300,
311
+ debug: !1
312
+ };
313
+ let y = d;
314
+ export {
315
+ y as PanelSet
316
+ };
317
+ //# sourceMappingURL=panelset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panelset.js","sources":["../src/lib/index.ts"],"sourcesContent":["import './styles/panelset.scss';\n\n// Configuration types\nexport interface PanelSetConfig {\n\ttransitions?: boolean | {\n\t\tpanels?: boolean;\n\t\theight?: boolean;\n\t};\n\tclosable?: boolean;\n\temptyPanelHeight?: number;\n\tloadingDelay?: number;\n\tdebug?: boolean;\n\tselector?: string;\n}\n\n// Event detail types\nexport interface ReadyEventDetail {\n\tcontainer: HTMLElement;\n\tinstance: PanelSet;\n}\n\nexport interface BeforeActivateEventDetail {\n\tpanelId: string;\n\ttargetPanel: HTMLElement;\n\toutgoingPanel: HTMLElement | null;\n\tsignal: AbortSignal;\n\tpromise: Promise<void> | null;\n}\n\nexport interface ActivationEventDetail {\n\tpanelId: string;\n\ttrigger: string | null;\n}\n\nexport interface ActivationAbortedEventDetail {\n\tpanelId: string;\n\ttrigger: string | null;\n}\n\n// Handler options\nexport interface HandlerOptions {\n\tonce?: boolean;\n}\n\n// Show options\nexport interface ShowOptions {\n\ttrigger?: string;\n}\n\n// Async content handler type\nexport type AsyncContentHandler = (\n\ttargetPanel: HTMLElement,\n\tsignal: AbortSignal\n) => Promise<void> | void;\n\n// Extend HTMLElement to include panelSet property\ndeclare global {\n\tinterface HTMLElement {\n\t\tpanelSet?: PanelSet;\n\t}\n}\n\nexport class PanelSet {\n\t// Default configuration\n\tstatic defaults: Required<Omit<PanelSetConfig, 'selector'>> = {\n\t\ttransitions: true,\n\t\tclosable: false,\n\t\temptyPanelHeight: 200,\n\t\tloadingDelay: 300,\n\t\tdebug: false\n\t};\n\n\t// Instance properties\n\telement!: HTMLElement;\n\tconfig!: Required<Omit<PanelSetConfig, 'selector'>>;\n\tpanels!: HTMLElement[];\n\tactivePanel!: HTMLElement;\n\tpanelWrapper!: HTMLElement;\n\tpendingPanel!: HTMLElement;\n\n\tprivate _openCloseGeneration: number = 0;\n\tprivate _isLoadingAsync: boolean = false;\n\t// private _isTransitioning: boolean = false;\n\tprivate _currentAbortController?: AbortController;\n\n\t// Parse data attributes from element\n\tstatic _getDataConfig(element: HTMLElement): Partial<PanelSetConfig> {\n\t\tconst data = element.dataset;\n\t\tconst config: Partial<PanelSetConfig> = {};\n\n\t\t// Define attribute types\n\t\tconst attrs: Record<string, 'json' | 'boolean' | 'number'> = {\n\t\t\ttransitions: 'json',\n\t\t\tclosable: 'boolean',\n\t\t\temptyPanelHeight: 'number',\n\t\t\tloadingDelay: 'number',\n\t\t\tdebug: 'boolean'\n\t\t};\n\n\t\t// Parse each attribute\n\t\tfor (const [key, type] of Object.entries(attrs)) {\n\t\t\tif (!(key in data)) continue;\n\n\t\t\tconst value = data[key];\n\t\t\tif (value === undefined) continue;\n\n\t\t\tswitch (type) {\n\t\t\t\tcase 'boolean':\n\t\t\t\t\t(config as any)[key] = value !== 'false';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'number':\n\t\t\t\t\t(config as any)[key] = parseInt(value, 10);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'json':\n\t\t\t\t\ttry {\n\t\t\t\t\t\t(config as any)[key] = JSON.parse(value);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Fall back to boolean parsing\n\t\t\t\t\t\t(config as any)[key] = value !== 'false';\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn config;\n\t}\n\n\t// Merge configurations\n\tstatic _mergeConfig(\n\t\tdefaults: Required<Omit<PanelSetConfig, 'selector'>>,\n\t\tdataConfig: Partial<PanelSetConfig>,\n\t\toptions: Partial<PanelSetConfig>\n\t): Required<Omit<PanelSetConfig, 'selector'>> {\n\t\treturn {\n\t\t\t...defaults,\n\t\t\t...dataConfig,\n\t\t\t...options\n\t\t} as Required<Omit<PanelSetConfig, 'selector'>>;\n\t}\n\n\t/**\n\t * Initialize PanelSet instances\n\t * @param selectorOrOptions - CSS selector string or config object\n\t * @param options - Additional config options (when first param is selector)\n\t * @returns Array of PanelSet instances\n\t */\n\tstatic init(selectorOrOptions: string | PanelSetConfig = {}, options: PanelSetConfig = {}): PanelSet[] {\n\t\t// Handle different call signatures\n\t\tlet selector: string;\n\t\tlet config: PanelSetConfig;\n\n\t\tif (typeof selectorOrOptions === 'string') {\n\t\t\t// init('#demo') or init('#demo', {debug: true})\n\t\t\tselector = selectorOrOptions;\n\t\t\tconfig = options;\n\t\t} else {\n\t\t\t// init() or init({selector: '#demo', debug: true})\n\t\t\tconfig = selectorOrOptions;\n\t\t\tselector = config.selector || '[data-panelset]';\n\t\t}\n\n\t\tconst elements = document.querySelectorAll<HTMLElement>(selector);\n\t\tconst instances: PanelSet[] = [];\n\n\t\telements.forEach(el => {\n\t\t\t// Skip if already initialized\n\t\t\tif (el.panelSet || el.dataset.panelset === 'true') {\n\t\t\t\tif (el.panelSet) instances.push(el.panelSet);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst instance = new PanelSet(el, config);\n\t\t\tinstances.push(instance);\n\t\t});\n\n\t\treturn instances;\n\t}\n\n\tconstructor(elementOrSelector: HTMLElement | string, options: PanelSetConfig = {}) {\n\t\t// Handle both element and selector\n\t\tlet element: HTMLElement | null;\n\t\tif (typeof elementOrSelector === 'string') {\n\t\t\telement = document.querySelector<HTMLElement>(elementOrSelector);\n\t\t\tif (!element) {\n\t\t\t\tthrow new Error(`PanelSet: No element found for selector \"${elementOrSelector}\"`);\n\t\t\t}\n\t\t} else {\n\t\t\telement = elementOrSelector;\n\t\t}\n\n\t\tthis.element = element;\n\n\t\t// Check if already initialized\n\t\tif (element.panelSet || element.dataset.panelset === 'true') {\n\t\t\tconsole.warn('PanelSet: Element already initialized, returning existing instance');\n\t\t\treturn element.panelSet!;\n\t\t}\n\n\t\t// Store instance on element\n\t\telement.panelSet = this;\n\n\t\tconst dataConfig = PanelSet._getDataConfig(element);\n\t\tthis.config = PanelSet._mergeConfig(\n\t\t\tPanelSet.defaults,\n\t\t\tdataConfig,\n\t\t\toptions\n\t\t);\n\n\t\tthis.panels = Array.from(element.querySelectorAll<HTMLElement>('[role=\"tabpanel\"]'));\n\t\tthis.activePanel =\n\t\t\tthis.panels.find(p => p.classList.contains('active')) || this.panels[0];\n\t\tthis.panelWrapper =\n\t\t\tthis.element.querySelector<HTMLElement>('.panel-wrapper') || this._autoWrapPanels();\n\n\t\tthis.pendingPanel = this.activePanel;\n\n\t\tthis._internalInit();\n\n\t\t// Mark as initialized, can be used in CSS selectors for visual confirmation\n\t\tthis.element.dataset.panelset = 'true';\n\n\t\tthis._log(`Initialized (${this.panels.length} panels)`);\n\t\tthis._dispatch<ReadyEventDetail>('ps:ready', { container: this.element, instance: this });\n\t}\n\n\t// Debug logging helper\n\tprivate _log(message: string): void {\n\t\tif (!this.config.debug) return;\n\t\tconst id = this.element.id || 'no id';\n\t\tconsole.log(`[PanelSet] - \"${id}\" -`, message);\n\t}\n\n\tprivate _autoWrapPanels(): HTMLElement {\n\t\tconst wrapper = document.createElement('div');\n\t\twrapper.className = 'panel-wrapper';\n\t\tthis.panels.forEach(panel => wrapper.appendChild(panel));\n\t\tthis.element.appendChild(wrapper);\n\t\treturn wrapper;\n\t}\n\n\tprivate _internalInit(): void {\n\t\tthis.panels.forEach(panel => {\n\t\t\tpanel.classList.remove('fade', 'incoming');\n\t\t\tif (panel !== this.activePanel) {\n\t\t\t\tpanel.hidden = true;\n\t\t\t\tpanel.classList.remove('active');\n\t\t\t} else {\n\t\t\t\tpanel.hidden = false;\n\t\t\t\tpanel.classList.add('active');\n\t\t\t}\n\t\t});\n\t\tthis.element.style.height = '';\n\t}\n\n\t// Dispatch custom event helper\n\tprivate _dispatch<T = unknown>(eventName: string, detail: T): void {\n\t\tthis.element.dispatchEvent(\n\t\t\tnew CustomEvent(eventName, {\n\t\t\t\tdetail,\n\t\t\t\tbubbles: true,\n\t\t\t\tcancelable: false\n\t\t\t})\n\t\t);\n\t}\n\n\t/* --- Modular helpers --- */\n\n\tprivate _getVerticalMetrics(el: HTMLElement | null): number {\n\t\tif (!el) return 0;\n\t\tconst s = getComputedStyle(el);\n\t\treturn ['paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth']\n\t\t\t.reduce((sum, prop) => sum + (parseFloat(s[prop as keyof CSSStyleDeclaration] as string) || 0), 0);\n\t}\n\n\tprivate _measureHeight(panel: HTMLElement): number {\n\t\tlet total = panel.offsetHeight;\n\t\ttotal += this._getVerticalMetrics(this.panelWrapper);\n\t\ttotal += this._getVerticalMetrics(this.element);\n\t\treturn total;\n\t}\n\n\tprivate _cleanupPanels(newPanel: HTMLElement): void {\n\t\tthis.panels.forEach(panel => {\n\t\t\tpanel.classList.remove('fade', 'incoming');\n\t\t\tif (panel !== newPanel) {\n\t\t\t\tpanel.classList.remove('active');\n\t\t\t\tpanel.hidden = true;\n\t\t\t} else {\n\t\t\t\tpanel.classList.add('active');\n\t\t\t\tpanel.hidden = false;\n\t\t\t}\n\t\t});\n\t\tthis.element.style.height = '';\n\t\tthis.element.classList.remove('is-transitioning');\n\t\tthis.activePanel = newPanel;\n\t}\n\n\tprivate _waitForTransition(element: HTMLElement): Promise<void> {\n\t\treturn new Promise(resolve => {\n\t\t\tconst styles = getComputedStyle(element);\n\t\t\tconst duration = parseFloat(styles.transitionDuration) || 0;\n\t\t\tconst delay = parseFloat(styles.transitionDelay) || 0;\n\t\t\tif (duration + delay === 0) {\n\t\t\t\tresolve();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst handler = (e: TransitionEvent) => {\n\t\t\t\tif (e.target !== element) return;\n\t\t\t\telement.removeEventListener('transitionend', handler);\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\telement.addEventListener('transitionend', handler);\n\t\t});\n\t}\n\n\t// Shared helper for open/close\n\tprivate _animateOpenClose(isOpening: boolean, withTransition: boolean): void {\n\t\tconst action = isOpening ? 'opening' : 'closing';\n\t\tconst oppositeAction = isOpening ? 'closing' : 'opening';\n\t\tconst oppositeClass = `is-${oppositeAction}`;\n\t\tconst actionClass = `is-${action}`;\n\n\t\tthis._log(isOpening ? 'Opening' : 'Closing');\n\n\t\tthis._openCloseGeneration++;\n\t\tconst myGeneration = this._openCloseGeneration;\n\n\t\t// Remove opposite state if interrupting\n\t\tif (this.element.classList.contains(oppositeClass)) {\n\t\t\tthis.element.classList.remove(oppositeClass);\n\t\t}\n\n\t\tconst targetHeight = isOpening ? this._measureHeight(this.pendingPanel) : 0;\n\n\t\tif (withTransition && this.config.transitions) {\n\t\t\tthis.element.classList.add(actionClass);\n\n\t\t\tconst currentHeight = this.element.offsetHeight;\n\t\t\tthis.element.style.height = `${currentHeight}px`;\n\n\t\t\tif (isOpening) this.element.classList.remove('is-closed');\n\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tthis.element.style.height = `${targetHeight}px`;\n\n\t\t\t\tthis._waitForTransition(this.element).then(() => {\n\t\t\t\t\tif (this._openCloseGeneration === myGeneration) {\n\t\t\t\t\t\tthis.element.style.height = '';\n\t\t\t\t\t\tthis.element.classList.remove(actionClass);\n\t\t\t\t\t\tif (!isOpening) this.element.classList.add('is-closed');\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t} else {\n\t\t\tif (isOpening) {\n\t\t\t\tthis.element.classList.remove('is-closed');\n\t\t\t} else {\n\t\t\t\tthis.element.classList.add('is-closed');\n\t\t\t}\n\t\t\tthis.element.style.height = '';\n\t\t}\n\t}\n\n\t/**\n\t * Get the ID of the currently active panel\n\t * @returns Panel ID or null if no panel is active\n\t */\n\tgetActive(): string | null {\n\t\treturn this.pendingPanel?.id || null;\n\t}\n\n\t/**\n\t * Open a closable panelset\n\t * @param withTransition - Whether to animate\n\t */\n\topen(withTransition: boolean = true): void {\n\t\tif (!this.config.closable) {\n\t\t\tthis._log('Cannot open: closable is false');\n\t\t\treturn;\n\t\t}\n\n\t\tconst isClosed = this.element.classList.contains('is-closed');\n\t\tconst isClosing = this.element.classList.contains('is-closing');\n\n\t\tif (!isClosed && !isClosing) return;\n\n\t\tthis._animateOpenClose(true, withTransition);\n\t}\n\n\t/**\n\t * Close a closable panelset\n\t * @param withTransition - Whether to animate\n\t */\n\tclose(withTransition: boolean = true): void {\n\t\tif (!this.config.closable) {\n\t\t\tthis._log('Cannot close: closable is false');\n\t\t\treturn;\n\t\t}\n\n\t\tconst isClosed = this.element.classList.contains('is-closed');\n\t\tconst isOpening = this.element.classList.contains('is-opening');\n\n\t\tif (isClosed && !isOpening) return;\n\n\t\tthis._animateOpenClose(false, withTransition);\n\t}\n\n\t/**\n\t * Toggle a closable panelset between open and closed\n\t * @param withTransition - Whether to animate\n\t */\n\ttoggle(withTransition: boolean = true): void {\n\t\tconst isClosed = this.element.classList.contains('is-closed');\n\t\tconst isClosing = this.element.classList.contains('is-closing');\n\n\t\t// If closed or closing, open it\n\t\tif (isClosed || isClosing) {\n\t\t\tthis.open(withTransition);\n\t\t} else {\n\t\t\tthis.close(withTransition);\n\t\t}\n\t}\n\n\t/**\n\t * Register a handler for async content loading\n\t * @param handler - Async content handler function\n\t * @param options - Handler options (once: whether to load only once)\n\t */\n\tonBeforeActivate(handler: AsyncContentHandler, options: HandlerOptions = {}): void {\n\t\tconst once = options.once === true; // Default: false (always reload)\n\n\t\tthis.element.addEventListener('ps:beforeactivate', (e) => {\n\t\t\tconst event = e as CustomEvent<BeforeActivateEventDetail>;\n\t\t\tconst { targetPanel, signal } = event.detail;\n\n\t\t\t// Skip if already loaded and once=true\n\t\t\tif (once && targetPanel.dataset.loaded === 'true') {\n\t\t\t\tif (this.config.debug) {\n\t\t\t\t\tthis._log(`Skipping ${targetPanel.id} (already loaded)`);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Call user handler\n\t\t\tconst result = handler(targetPanel, signal);\n\n\t\t\t// If handler returns a promise, attach it to the event\n\t\t\tif (result && typeof result.then === 'function') {\n\t\t\t\tevent.detail.promise = result\n\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t// Auto-mark as loaded on success if once=true\n\t\t\t\t\t\tif (once) {\n\t\t\t\t\t\t\ttargetPanel.dataset.loaded = 'true';\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch(error => {\n\t\t\t\t\t\tif (error.name === 'AbortError') {\n\t\t\t\t\t\t\tif (this.config.debug) {\n\t\t\t\t\t\t\t\tthis._log(`Load aborted: ${targetPanel.id}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis._log(`Load failed: ${error.message}`);\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\t/* --- Main logic --- */\n\n\t/**\n\t * Show a panel by ID\n\t * @param panelId - ID of the panel to show\n\t * @param withTransition - Whether to animate the transition\n\t * @param options - Additional options (trigger name)\n\t */\n\tasync show(panelId: string, withTransition: boolean = true, options: ShowOptions = {}): Promise<void> {\n\t\tconst newPanel = this.panels.find(p => p.id === panelId);\n\n\t\tif (!newPanel) {\n\t\t\tthis._log(`Panel not found: ${panelId}`);\n\t\t\treturn;\n\t\t}\n\n\t\tif (newPanel === this.pendingPanel) return;\n\n\t\tconst prevPanel = this.pendingPanel;\n\t\tconst prevPanelId = prevPanel?.id;\n\t\tthis.pendingPanel = newPanel;\n\n\t\tthis.element.classList.remove('is-loading');\n\n\t\tif (prevPanel && prevPanel !== this.activePanel && prevPanel !== newPanel) {\n\t\t\tprevPanel.classList.remove('incoming');\n\t\t\tif (prevPanel.hidden) {\n\t\t\t\t// Was never visible, keep hidden\n\t\t\t} else {\n\t\t\t\tprevPanel.classList.remove('active');\n\t\t\t}\n\t\t}\n\n\t\tconst wasLoadingAsync = this._isLoadingAsync;\n\n\t\tif (this._currentAbortController) {\n\t\t\tthis._currentAbortController.abort();\n\n\t\t\t// Only fire abort if we're cancelling an async operation\n\t\t\tif (wasLoadingAsync && prevPanelId && prevPanelId !== panelId) {\n\t\t\t\tthis._dispatch<ActivationAbortedEventDetail>('ps:activationaborted', {\n\t\t\t\t\tpanelId: prevPanelId,\n\t\t\t\t\ttrigger: null\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\t\tthis._currentAbortController = abortController;\n\t\tthis._isLoadingAsync = false;\n\n\t\tthis._log(`${prevPanel?.id || 'none'} → ${panelId}`);\n\n\t\tconst beforeActivateDetail: BeforeActivateEventDetail = {\n\t\t\tpanelId,\n\t\t\ttargetPanel: newPanel,\n\t\t\toutgoingPanel: prevPanel,\n\t\t\tsignal: abortController.signal,\n\t\t\tpromise: null\n\t\t};\n\n\t\tconst beforeActivateEvent = new CustomEvent('ps:beforeactivate', {\n\t\t\tdetail: beforeActivateDetail,\n\t\t\tbubbles: true,\n\t\t\tcancelable: false\n\t\t});\n\n\t\tthis.element.dispatchEvent(beforeActivateEvent);\n\n\t\tconst userPromise = beforeActivateDetail.promise;\n\n\t\tif (userPromise) {\n\t\t\tthis._isLoadingAsync = true;\n\t\t\tthis._log('Waiting for content...');\n\n\t\t\tlet spinnerTimeout: ReturnType<typeof setTimeout> | undefined;\n\t\t\tlet loadingShown = false;\n\n\t\t\tif (this.config.loadingDelay > 0) {\n\t\t\t\tspinnerTimeout = setTimeout(() => {\n\t\t\t\t\tthis.element.classList.add('is-loading');\n\t\t\t\t\tloadingShown = true;\n\t\t\t\t}, this.config.loadingDelay);\n\t\t\t} else {\n\t\t\t\tthis.element.classList.add('is-loading');\n\t\t\t\tloadingShown = true;\n\t\t\t}\n\n\t\t\tconst hasPreviousPanel = this.activePanel && this.activePanel !== newPanel;\n\n\t\t\tif (!hasPreviousPanel) {\n\t\t\t\tconst shouldTransition = withTransition !== false && this.config.transitions !== false;\n\t\t\t\tlet heightTransition = shouldTransition;\n\t\t\t\tif (typeof this.config.transitions === 'object') {\n\t\t\t\t\theightTransition = shouldTransition && this.config.transitions.height !== false;\n\t\t\t\t}\n\n\t\t\t\tif (heightTransition) {\n\t\t\t\t\tconst currentHeight = this.element.offsetHeight;\n\t\t\t\t\tthis.element.style.height = `${currentHeight}px`;\n\n\t\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\t\tthis.element.style.height = `${this.config.emptyPanelHeight}px`;\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait userPromise;\n\n\t\t\t\tif (spinnerTimeout) clearTimeout(spinnerTimeout);\n\n\t\t\t\tif (abortController.signal.aborted) {\n\t\t\t\t\tthis._log(`Aborted during load: ${panelId}`);\n\t\t\t\t\tif (loadingShown) this.element.classList.remove('is-loading');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._log('Content loaded');\n\t\t\t} catch (error) {\n\t\t\t\tif (spinnerTimeout) clearTimeout(spinnerTimeout);\n\n\t\t\t\tconst err = error as Error;\n\t\t\t\tthis._log(`Load failed: ${err.message}`);\n\t\t\t\tif (loadingShown) this.element.classList.remove('is-loading');\n\n\t\t\t\tif (err.name !== 'AbortError') {\n\t\t\t\t\tconsole.error('Panel load error:', error);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (loadingShown) this.element.classList.remove('is-loading');\n\t\t}\n\n\t\tif (abortController.signal.aborted) {\n\t\t\tthis._log(`Aborted: ${panelId}`);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._dispatch<ActivationEventDetail>('ps:activationstart', {\n\t\t\tpanelId,\n\t\t\ttrigger: options.trigger || null\n\t\t});\n\n\t\tconst shouldTransition = withTransition !== false && this.config.transitions !== false;\n\n\t\tlet panelTransition = shouldTransition;\n\t\tlet heightTransition = shouldTransition;\n\n\t\tif (typeof this.config.transitions === 'object') {\n\t\t\tpanelTransition = shouldTransition && this.config.transitions.panels !== false;\n\t\t\theightTransition = shouldTransition && this.config.transitions.height !== false;\n\t\t}\n\n\t\tthis.panels.forEach(panel => panel.classList.toggle('fade', panelTransition));\n\n\t\tconst startHeight = this.element.offsetHeight;\n\t\tif (heightTransition) {\n\t\t\tthis.element.style.height = `${startHeight}px`;\n\t\t}\n\n\t\tconst outgoingPanel = this.activePanel;\n\n\t\tnewPanel.hidden = false;\n\t\tnewPanel.classList.add('incoming');\n\t\tif (panelTransition) {\n\t\t\t// Apply general transitioning class for overflow: hidden\n\t\t\tthis.element.classList.add('is-transitioning');\n\t\t}\n\t\tif (outgoingPanel && outgoingPanel !== newPanel) {\n\t\t\toutgoingPanel.classList.remove('active', 'incoming');\n\t\t\toutgoingPanel.hidden = false;\n\t\t}\n\n\t\trequestAnimationFrame(() => {\n\t\t\tnewPanel.classList.add('active');\n\t\t\tif (outgoingPanel && outgoingPanel !== newPanel) {\n\t\t\t\toutgoingPanel.classList.remove('incoming');\n\t\t\t}\n\n\t\t\tconst targetHeight = this._measureHeight(newPanel);\n\t\t\tconst heightChanged = startHeight !== targetHeight;\n\n\t\t\tif (heightTransition) {\n\t\t\t\tthis.element.style.height = `${targetHeight}px`;\n\t\t\t}\n\n\t\t\tconst promises: Promise<void>[] = [];\n\t\t\tif (panelTransition) {\n\t\t\t\tpromises.push(this._waitForTransition(newPanel));\n\t\t\t}\n\t\t\tif (heightTransition && heightChanged) {\n\t\t\t\tpromises.push(this._waitForTransition(this.element));\n\t\t\t}\n\t\t\tif (!promises.length) promises.push(Promise.resolve());\n\n\t\t\tPromise.all(promises).then(() => {\n\t\t\t\t// Check if interrupted by another activation\n\t\t\t\tif (this.pendingPanel !== newPanel) {\n\t\t\t\t\tthis._log(`Interrupted: ${panelId}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._cleanupPanels(newPanel);\n\t\t\t\tthis._log(`✓ ${panelId}`);\n\t\t\t\tthis._dispatch<ActivationEventDetail>('ps:activationcomplete', {\n\t\t\t\t\tpanelId,\n\t\t\t\t\ttrigger: options.trigger || null\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t}\n}"],"names":["_PanelSet","elementOrSelector","options","element","dataConfig","p","data","config","attrs","key","type","value","defaults","selectorOrOptions","selector","elements","instances","el","instance","message","id","wrapper","panel","eventName","detail","s","sum","prop","total","newPanel","resolve","styles","duration","delay","handler","e","isOpening","withTransition","action","oppositeClass","actionClass","myGeneration","targetHeight","currentHeight","isClosed","isClosing","once","event","targetPanel","signal","result","error","panelId","prevPanel","prevPanelId","wasLoadingAsync","abortController","beforeActivateDetail","beforeActivateEvent","userPromise","spinnerTimeout","loadingShown","shouldTransition","heightTransition","err","panelTransition","startHeight","outgoingPanel","heightChanged","promises","PanelSet"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "panelset",
3
+ "version": "0.5.0",
4
+ "description": "Flexible panel management with smooth transitions",
5
+ "type": "module",
6
+ "main": "./dist/panelset.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/panelset.js"
12
+ },
13
+ "./dist/*.css": "./dist/*.css"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "start": "npm run dev",
22
+ "dev": "vite --config vite.config.docs.js",
23
+ "build": "npm run clean && npm run build:lib && npm run build:docs && npm run copy-lib",
24
+ "build:lib": "vite build",
25
+ "build:docs": "vite build --config vite.config.docs.js",
26
+ "copy-lib": "mkdir -p docs/lib && cp -r dist/* docs/lib/",
27
+ "preview:docs": "npm run build && vite preview --config vite.config.docs.js",
28
+ "clean": "rm -rf dist docs"
29
+ },
30
+ "keywords": [
31
+ "panels",
32
+ "tabs",
33
+ "accordion",
34
+ "transitions",
35
+ "ui-components"
36
+ ],
37
+ "author": "Martijn De Jongh",
38
+ "license": "MIT",
39
+ "devDependencies": {
40
+ "@vituum/vite-plugin-pug": "^1.1.0",
41
+ "fast-glob": "^3.3.3",
42
+ "sass": "latest",
43
+ "terser": "^5.44.1",
44
+ "typescript": "^5.3.0",
45
+ "vite": "^7.2.2",
46
+ "vite-plugin-dts": "^4.5.4",
47
+ "vituum": "^1.2.0"
48
+ }
49
+ }
@@ -0,0 +1,44 @@
1
+
2
+ // Copy to clipboard functionality for code blocks
3
+ document.addEventListener('click', async (e) => {
4
+ const button = e.target.closest('button.copy');
5
+ if (!button) return;
6
+
7
+ const demo = button.closest('.code-block');
8
+ if (!demo) return;
9
+
10
+ const codeContainer = demo.querySelector('pre code');
11
+ if (!codeContainer) return;
12
+
13
+ let content = null;
14
+
15
+ const hljsTable = codeContainer.querySelector("table.hljs-ln");
16
+ if (hljsTable) {
17
+ // Extract only code content, not line numbers
18
+ content = Array.from(hljsTable.querySelectorAll("td.hljs-ln-code"))
19
+ .map((cell) => cell.textContent)
20
+ .join("\n");
21
+ } else {
22
+ content = codeContainer.textContent.replace(/^\s+|\s+$/g, "");
23
+ }
24
+
25
+ const originalContent = button.innerHTML;
26
+
27
+ try {
28
+ await navigator.clipboard.writeText(content);
29
+
30
+ button.innerHTML = 'Copied!';
31
+ button.classList.add('copied');
32
+
33
+ } catch (err) {
34
+ console.error('Failed to copy text: ', err);
35
+ button.innerHTML = 'Failed!';
36
+ button.classList.add('error');
37
+
38
+ } finally {
39
+ setTimeout(() => {
40
+ button.innerHTML = originalContent;
41
+ button.classList.remove('copied', 'error');
42
+ }, 1000);
43
+ }
44
+ });