@nuralyui/panel 0.0.1 → 0.0.4
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/bundle.js +1076 -0
- package/index.d.ts +9 -0
- package/index.js +9 -0
- package/index.js.map +1 -0
- package/package.json +45 -45
- package/panel.component.d.ts +199 -0
- package/panel.component.js +720 -0
- package/panel.component.js.map +1 -0
- package/panel.style.d.ts +7 -0
- package/panel.style.js +498 -0
- package/panel.style.js.map +1 -0
- package/panel.types.d.ts +94 -0
- package/panel.types.js +50 -0
- package/panel.types.js.map +1 -0
- package/react.d.ts +11 -0
- package/react.js +18 -0
- package/react.js.map +1 -0
- package/README.md +0 -218
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
7
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
9
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
10
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
11
|
+
};
|
|
12
|
+
import { html, LitElement, nothing } from 'lit';
|
|
13
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
14
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
15
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
16
|
+
import { PanelMode, PanelSize, PanelPosition, MaximizePosition, EMPTY_STRING } from './panel.types.js';
|
|
17
|
+
import { styles } from './panel.style.js';
|
|
18
|
+
import { NuralyUIBaseMixin } from '@nuralyui/common/mixins';
|
|
19
|
+
// Import icon and label components
|
|
20
|
+
import '../icon/index.js';
|
|
21
|
+
import '../label/index.js';
|
|
22
|
+
// Import controllers
|
|
23
|
+
import { PanelDragController, PanelResizeController } from './controllers/index.js';
|
|
24
|
+
/**
|
|
25
|
+
* Versatile panel component that can transform between panel and window modes.
|
|
26
|
+
*
|
|
27
|
+
* Features:
|
|
28
|
+
* - Transform between panel (docked) and window (floating) modes
|
|
29
|
+
* - Draggable in window mode
|
|
30
|
+
* - Resizable panels
|
|
31
|
+
* - Collapsible content
|
|
32
|
+
* - Minimizable to compact view
|
|
33
|
+
* - Theme-aware styling with light/dark mode support
|
|
34
|
+
* - Multiple size presets
|
|
35
|
+
* - Positioned docking (left, right, top, bottom)
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```html
|
|
39
|
+
* <!-- Panel docked to right side -->
|
|
40
|
+
* <nr-panel
|
|
41
|
+
* title="Settings Panel"
|
|
42
|
+
* mode="panel"
|
|
43
|
+
* position="right"
|
|
44
|
+
* size="medium">
|
|
45
|
+
* <p>Panel content here</p>
|
|
46
|
+
* </nr-panel>
|
|
47
|
+
*
|
|
48
|
+
* <!-- Floating draggable window -->
|
|
49
|
+
* <nr-panel
|
|
50
|
+
* title="Tool Window"
|
|
51
|
+
* mode="window"
|
|
52
|
+
* draggable
|
|
53
|
+
* resizable
|
|
54
|
+
* size="medium">
|
|
55
|
+
* <p>Window content here</p>
|
|
56
|
+
* </nr-panel>
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @fires panel-mode-change - Panel mode changed
|
|
60
|
+
* @fires panel-close - Panel closed
|
|
61
|
+
* @fires panel-minimize - Panel minimized
|
|
62
|
+
* @fires panel-maximize - Panel maximized/restored
|
|
63
|
+
* @fires panel-drag-start - Panel drag started
|
|
64
|
+
* @fires panel-drag-end - Panel drag ended
|
|
65
|
+
* @fires panel-resize - Panel resized
|
|
66
|
+
*
|
|
67
|
+
* @slot default - Panel body content
|
|
68
|
+
* @slot header - Custom header content
|
|
69
|
+
* @slot footer - Custom footer content
|
|
70
|
+
*/
|
|
71
|
+
let NrPanelElement = class NrPanelElement extends NuralyUIBaseMixin(LitElement) {
|
|
72
|
+
constructor() {
|
|
73
|
+
super(...arguments);
|
|
74
|
+
this.requiredComponents = ['nr-icon', 'nr-label'];
|
|
75
|
+
/** Panel mode (panel, window, minimized) */
|
|
76
|
+
this.mode = PanelMode.Panel;
|
|
77
|
+
/** Panel size */
|
|
78
|
+
this.size = PanelSize.Medium;
|
|
79
|
+
/** Panel position (for panel mode) */
|
|
80
|
+
this.position = PanelPosition.Right;
|
|
81
|
+
/** Position where the window appears when maximizing from embedded mode */
|
|
82
|
+
this.maximizePosition = MaximizePosition.Center;
|
|
83
|
+
/** Whether the panel can be dragged (window mode only) */
|
|
84
|
+
this.draggable = true;
|
|
85
|
+
/** Whether the panel is resizable */
|
|
86
|
+
this.resizable = false;
|
|
87
|
+
/** Whether the panel content can be collapsed */
|
|
88
|
+
this.collapsible = false;
|
|
89
|
+
/** Whether the panel can be minimized */
|
|
90
|
+
this.minimizable = true;
|
|
91
|
+
/** Whether the panel can be closed */
|
|
92
|
+
this.closable = false;
|
|
93
|
+
/** Whether to enable smooth animations for position/mode changes */
|
|
94
|
+
this.animated = false;
|
|
95
|
+
/** Panel title */
|
|
96
|
+
this.title = EMPTY_STRING;
|
|
97
|
+
/** Header icon */
|
|
98
|
+
this.icon = EMPTY_STRING;
|
|
99
|
+
/** Custom width */
|
|
100
|
+
this.width = EMPTY_STRING;
|
|
101
|
+
/** Custom height */
|
|
102
|
+
this.height = EMPTY_STRING;
|
|
103
|
+
/** Whether the panel is open/visible */
|
|
104
|
+
this.open = true;
|
|
105
|
+
/** Collapsed state */
|
|
106
|
+
this.collapsed = false;
|
|
107
|
+
/** Dragging state */
|
|
108
|
+
this.isDragging = false;
|
|
109
|
+
/** Animation state */
|
|
110
|
+
this.animating = false;
|
|
111
|
+
/** Current X offset for dragging */
|
|
112
|
+
this.offsetX = 0;
|
|
113
|
+
/** Current Y offset for dragging */
|
|
114
|
+
this.offsetY = 0;
|
|
115
|
+
/** Current panel width */
|
|
116
|
+
this.panelWidth = 0;
|
|
117
|
+
/** Current panel height */
|
|
118
|
+
this.panelHeight = 0;
|
|
119
|
+
/** Original mode before any transformations (for restoration from minimized) */
|
|
120
|
+
this.originalMode = null;
|
|
121
|
+
/** Track if panel is maximized from embedded mode */
|
|
122
|
+
this.isMaximizedFromEmbedded = false;
|
|
123
|
+
/** Track if this panel was created from a tab pop-out */
|
|
124
|
+
this.isTabPopOut = false;
|
|
125
|
+
/** Track if this is the first update to capture initial mode */
|
|
126
|
+
this.isFirstUpdate = true;
|
|
127
|
+
/** Original dimensions before maximizing from embedded mode */
|
|
128
|
+
this.originalEmbeddedWidth = 0;
|
|
129
|
+
/** Original dimensions before maximizing from embedded mode */
|
|
130
|
+
this.originalEmbeddedHeight = 0;
|
|
131
|
+
// Controllers
|
|
132
|
+
this.dragController = new PanelDragController(this);
|
|
133
|
+
// @ts-ignore - Controller handles events through listeners, doesn't need direct reference
|
|
134
|
+
this._resizeController = new PanelResizeController(this);
|
|
135
|
+
}
|
|
136
|
+
connectedCallback() {
|
|
137
|
+
super.connectedCallback();
|
|
138
|
+
this.validateDependencies();
|
|
139
|
+
this.animating = true;
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
this.animating = false;
|
|
142
|
+
}, 300);
|
|
143
|
+
}
|
|
144
|
+
willUpdate(changedProperties) {
|
|
145
|
+
super.willUpdate(changedProperties);
|
|
146
|
+
// Capture the original mode on first update
|
|
147
|
+
if (this.isFirstUpdate && this.mode) {
|
|
148
|
+
this.originalMode = this.mode;
|
|
149
|
+
this.isFirstUpdate = false;
|
|
150
|
+
}
|
|
151
|
+
// Track mode changes
|
|
152
|
+
if (changedProperties.has('mode')) {
|
|
153
|
+
const oldMode = changedProperties.get('mode');
|
|
154
|
+
if (oldMode && oldMode !== this.mode) {
|
|
155
|
+
this.handleModeChange(oldMode);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
updated(changedProperties) {
|
|
160
|
+
super.updated(changedProperties);
|
|
161
|
+
// If mode changed, give the DOM time to update with new classes for drag handlers
|
|
162
|
+
if (changedProperties.has('mode')) {
|
|
163
|
+
requestAnimationFrame(() => {
|
|
164
|
+
this.requestUpdate();
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Handle mode change
|
|
170
|
+
*/
|
|
171
|
+
handleModeChange(oldMode) {
|
|
172
|
+
this.animating = true;
|
|
173
|
+
// Reset position when:
|
|
174
|
+
// 1. Switching to window mode from panel mode
|
|
175
|
+
// 2. Restoring from minimized mode back to window mode
|
|
176
|
+
if (this.mode === PanelMode.Window &&
|
|
177
|
+
(oldMode === PanelMode.Panel || oldMode === PanelMode.Minimized)) {
|
|
178
|
+
this.dragController.resetPosition();
|
|
179
|
+
}
|
|
180
|
+
// Dispatch mode change event
|
|
181
|
+
this.dispatchEvent(new CustomEvent('panel-mode-change', {
|
|
182
|
+
bubbles: true,
|
|
183
|
+
detail: { mode: this.mode, previousMode: oldMode }
|
|
184
|
+
}));
|
|
185
|
+
setTimeout(() => {
|
|
186
|
+
this.animating = false;
|
|
187
|
+
}, 300);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Transform to window mode
|
|
191
|
+
*/
|
|
192
|
+
transformToWindow() {
|
|
193
|
+
if (this.mode !== PanelMode.Window) {
|
|
194
|
+
this.mode = PanelMode.Window;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Transform to panel mode
|
|
199
|
+
*/
|
|
200
|
+
transformToPanel() {
|
|
201
|
+
if (this.mode !== PanelMode.Panel) {
|
|
202
|
+
this.mode = PanelMode.Panel;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Restore panel to its original state
|
|
207
|
+
*/
|
|
208
|
+
restore() {
|
|
209
|
+
// Special handling for tab pop-out panels
|
|
210
|
+
if (this.isTabPopOut) {
|
|
211
|
+
console.log('[Panel] Restore called for tab pop-out panel');
|
|
212
|
+
// For tab pop-outs, "restore" means pop back in
|
|
213
|
+
// Dispatch event first to trigger the pop-in logic
|
|
214
|
+
this.dispatchEvent(new CustomEvent('panel-restore', { bubbles: true }));
|
|
215
|
+
// Then close the panel (the pop-in logic should handle tab restoration)
|
|
216
|
+
// Use setTimeout to ensure the event is processed first
|
|
217
|
+
setTimeout(() => {
|
|
218
|
+
this.open = false;
|
|
219
|
+
this.dispatchEvent(new CustomEvent('panel-close', { bubbles: true }));
|
|
220
|
+
}, 0);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// Special handling for panels maximized from embedded mode
|
|
224
|
+
if (this.isMaximizedFromEmbedded) {
|
|
225
|
+
// Restore back to embedded mode
|
|
226
|
+
this.restoreEmbedded();
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
// Default restore behavior (same as maximize for minimized panels)
|
|
230
|
+
this.maximize();
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Minimize panel
|
|
234
|
+
*/
|
|
235
|
+
minimize() {
|
|
236
|
+
if (!this.minimizable)
|
|
237
|
+
return;
|
|
238
|
+
this.mode = PanelMode.Minimized;
|
|
239
|
+
this.dispatchEvent(new CustomEvent('panel-minimize', { bubbles: true }));
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Maximize embedded panel to floating window
|
|
243
|
+
*/
|
|
244
|
+
maximizeEmbedded() {
|
|
245
|
+
var _a;
|
|
246
|
+
console.log('[Panel] === MAXIMIZE EMBEDDED START ===');
|
|
247
|
+
console.log('[Panel] Current mode:', this.mode);
|
|
248
|
+
console.log('[Panel] this.panelWidth:', this.panelWidth);
|
|
249
|
+
console.log('[Panel] this.panelHeight:', this.panelHeight);
|
|
250
|
+
if (this.mode !== PanelMode.Embedded) {
|
|
251
|
+
console.log('[Panel] ✗ Not in embedded mode, aborting');
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// Store original dimensions before maximizing
|
|
255
|
+
const panel = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.panel');
|
|
256
|
+
if (panel) {
|
|
257
|
+
console.log('[Panel] Panel DOM element found');
|
|
258
|
+
console.log('[Panel] panel.offsetWidth:', panel.offsetWidth);
|
|
259
|
+
console.log('[Panel] panel.offsetHeight:', panel.offsetHeight);
|
|
260
|
+
// Use current tracked dimensions or fall back to DOM dimensions
|
|
261
|
+
this.originalEmbeddedWidth = this.panelWidth > 0 ? this.panelWidth : panel.offsetWidth;
|
|
262
|
+
this.originalEmbeddedHeight = this.panelHeight > 0 ? this.panelHeight : panel.offsetHeight;
|
|
263
|
+
console.log('[Panel] ✓ Set originalEmbeddedWidth:', this.originalEmbeddedWidth);
|
|
264
|
+
console.log('[Panel] ✓ Set originalEmbeddedHeight:', this.originalEmbeddedHeight);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
console.warn('[Panel] ✗ Panel DOM element not found');
|
|
268
|
+
}
|
|
269
|
+
// Set the original mode to embedded so we can restore properly
|
|
270
|
+
if (!this.originalMode || this.originalMode !== PanelMode.Embedded) {
|
|
271
|
+
this.originalMode = PanelMode.Embedded;
|
|
272
|
+
}
|
|
273
|
+
this.isMaximizedFromEmbedded = true;
|
|
274
|
+
// Keep the original dimensions when maximizing to window mode
|
|
275
|
+
this.panelWidth = this.originalEmbeddedWidth;
|
|
276
|
+
this.panelHeight = this.originalEmbeddedHeight;
|
|
277
|
+
console.log('[Panel] Final panelWidth:', this.panelWidth);
|
|
278
|
+
console.log('[Panel] Final panelHeight:', this.panelHeight);
|
|
279
|
+
this.mode = PanelMode.Window;
|
|
280
|
+
console.log('[Panel] Mode changed to Window');
|
|
281
|
+
// Wait for the mode change to render, then set position
|
|
282
|
+
this.updateComplete.then(() => {
|
|
283
|
+
this.setMaximizePosition();
|
|
284
|
+
});
|
|
285
|
+
this.dispatchEvent(new CustomEvent('panel-maximize-embedded', {
|
|
286
|
+
bubbles: true,
|
|
287
|
+
detail: { mode: this.mode, position: this.maximizePosition }
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Set the window position based on maximizePosition with slight randomization
|
|
292
|
+
*/
|
|
293
|
+
setMaximizePosition() {
|
|
294
|
+
var _a;
|
|
295
|
+
const panel = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.panel');
|
|
296
|
+
if (!panel)
|
|
297
|
+
return;
|
|
298
|
+
const rect = panel.getBoundingClientRect();
|
|
299
|
+
const viewportWidth = window.innerWidth;
|
|
300
|
+
const viewportHeight = window.innerHeight;
|
|
301
|
+
// Add random offset for cascading effect (±80px for X, ±60px for Y)
|
|
302
|
+
const randomX = Math.floor(Math.random() * 160) - 80;
|
|
303
|
+
const randomY = Math.floor(Math.random() * 120) - 60;
|
|
304
|
+
console.log('[Panel] setMaximizePosition - randomX:', randomX, 'randomY:', randomY);
|
|
305
|
+
console.log('[Panel] maximizePosition:', this.maximizePosition);
|
|
306
|
+
switch (this.maximizePosition) {
|
|
307
|
+
case MaximizePosition.Center:
|
|
308
|
+
// Center with randomization for cascading effect
|
|
309
|
+
this.offsetX = randomX;
|
|
310
|
+
this.offsetY = randomY;
|
|
311
|
+
console.log('[Panel] Set offsetX:', this.offsetX, 'offsetY:', this.offsetY);
|
|
312
|
+
break;
|
|
313
|
+
case MaximizePosition.Left:
|
|
314
|
+
this.offsetX = -(viewportWidth / 2 - rect.width / 2 - 40) + randomX;
|
|
315
|
+
this.offsetY = randomY;
|
|
316
|
+
break;
|
|
317
|
+
case MaximizePosition.Right:
|
|
318
|
+
this.offsetX = (viewportWidth / 2 - rect.width / 2 - 40) + randomX;
|
|
319
|
+
this.offsetY = randomY;
|
|
320
|
+
break;
|
|
321
|
+
case MaximizePosition.TopLeft:
|
|
322
|
+
this.offsetX = -(viewportWidth / 2 - rect.width / 2 - 40) + randomX;
|
|
323
|
+
this.offsetY = -(viewportHeight / 2 - rect.height / 2 - 40) + randomY;
|
|
324
|
+
break;
|
|
325
|
+
case MaximizePosition.TopRight:
|
|
326
|
+
this.offsetX = (viewportWidth / 2 - rect.width / 2 - 40) + randomX;
|
|
327
|
+
this.offsetY = -(viewportHeight / 2 - rect.height / 2 - 40) + randomY;
|
|
328
|
+
break;
|
|
329
|
+
case MaximizePosition.BottomLeft:
|
|
330
|
+
this.offsetX = -(viewportWidth / 2 - rect.width / 2 - 40) + randomX;
|
|
331
|
+
this.offsetY = (viewportHeight / 2 - rect.height / 2 - 40) + randomY;
|
|
332
|
+
break;
|
|
333
|
+
case MaximizePosition.BottomRight:
|
|
334
|
+
this.offsetX = (viewportWidth / 2 - rect.width / 2 - 40) + randomX;
|
|
335
|
+
this.offsetY = (viewportHeight / 2 - rect.height / 2 - 40) + randomY;
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
this.requestUpdate();
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Restore maximized embedded panel back to embedded mode
|
|
342
|
+
*/
|
|
343
|
+
restoreEmbedded() {
|
|
344
|
+
if (!this.isMaximizedFromEmbedded)
|
|
345
|
+
return;
|
|
346
|
+
this.isMaximizedFromEmbedded = false;
|
|
347
|
+
this.mode = PanelMode.Embedded;
|
|
348
|
+
this.offsetX = 0;
|
|
349
|
+
this.offsetY = 0;
|
|
350
|
+
// Restore original dimensions
|
|
351
|
+
this.panelWidth = this.originalEmbeddedWidth;
|
|
352
|
+
this.panelHeight = this.originalEmbeddedHeight;
|
|
353
|
+
// Remove transform and apply restored dimensions
|
|
354
|
+
requestAnimationFrame(() => {
|
|
355
|
+
var _a;
|
|
356
|
+
const panel = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.panel');
|
|
357
|
+
if (panel) {
|
|
358
|
+
panel.style.transform = '';
|
|
359
|
+
// Apply the original dimensions
|
|
360
|
+
if (this.originalEmbeddedWidth > 0) {
|
|
361
|
+
panel.style.width = `${this.originalEmbeddedWidth}px`;
|
|
362
|
+
}
|
|
363
|
+
if (this.originalEmbeddedHeight > 0) {
|
|
364
|
+
panel.style.height = `${this.originalEmbeddedHeight}px`;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
this.dispatchEvent(new CustomEvent('panel-restore-embedded', {
|
|
369
|
+
bubbles: true,
|
|
370
|
+
detail: { mode: this.mode }
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Maximize/restore panel
|
|
375
|
+
*/
|
|
376
|
+
maximize() {
|
|
377
|
+
if (this.mode === PanelMode.Minimized) {
|
|
378
|
+
// When restoring from minimized, check if we were maximized from embedded
|
|
379
|
+
if (this.isMaximizedFromEmbedded) {
|
|
380
|
+
// Restore back to embedded mode instead of going to original mode
|
|
381
|
+
this.restoreEmbedded();
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
// Normal restoration: go back to the original mode
|
|
385
|
+
// (the mode before any transformations)
|
|
386
|
+
this.mode = this.originalMode || PanelMode.Panel;
|
|
387
|
+
// Reset offset values
|
|
388
|
+
this.offsetX = 0;
|
|
389
|
+
this.offsetY = 0;
|
|
390
|
+
// Remove any transform - let CSS handle positioning based on mode
|
|
391
|
+
requestAnimationFrame(() => {
|
|
392
|
+
var _a;
|
|
393
|
+
const panel = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.panel');
|
|
394
|
+
if (panel) {
|
|
395
|
+
panel.style.transform = '';
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
this.requestUpdate();
|
|
399
|
+
}
|
|
400
|
+
this.dispatchEvent(new CustomEvent('panel-maximize', { bubbles: true }));
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Close panel
|
|
404
|
+
*/
|
|
405
|
+
close() {
|
|
406
|
+
if (!this.closable)
|
|
407
|
+
return;
|
|
408
|
+
this.open = false;
|
|
409
|
+
this.dispatchEvent(new CustomEvent('panel-close', { bubbles: true }));
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Toggle collapsed state
|
|
413
|
+
*/
|
|
414
|
+
toggleCollapse() {
|
|
415
|
+
if (!this.collapsible)
|
|
416
|
+
return;
|
|
417
|
+
this.collapsed = !this.collapsed;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Get label size based on panel size
|
|
421
|
+
*/
|
|
422
|
+
getLabelSize() {
|
|
423
|
+
switch (this.size) {
|
|
424
|
+
case PanelSize.Small:
|
|
425
|
+
return 'small';
|
|
426
|
+
case PanelSize.Large:
|
|
427
|
+
return 'large';
|
|
428
|
+
case PanelSize.Medium:
|
|
429
|
+
case PanelSize.Custom:
|
|
430
|
+
default:
|
|
431
|
+
return 'medium';
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Get panel classes
|
|
436
|
+
*/
|
|
437
|
+
getPanelClasses() {
|
|
438
|
+
return {
|
|
439
|
+
'panel': true,
|
|
440
|
+
[`panel--mode-${this.mode}`]: true,
|
|
441
|
+
[`panel--position-${this.position}`]: this.mode === PanelMode.Panel,
|
|
442
|
+
[`panel--size-${this.size}`]: this.size !== PanelSize.Custom,
|
|
443
|
+
'panel--collapsed': this.collapsed,
|
|
444
|
+
'panel--dragging': this.isDragging,
|
|
445
|
+
'panel--animating': this.animating,
|
|
446
|
+
'panel--animated': this.animated
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Get panel styles
|
|
451
|
+
*/
|
|
452
|
+
getPanelStyles() {
|
|
453
|
+
const styles = {};
|
|
454
|
+
if (this.width) {
|
|
455
|
+
styles.width = this.width;
|
|
456
|
+
}
|
|
457
|
+
if (this.height) {
|
|
458
|
+
styles.height = this.height;
|
|
459
|
+
}
|
|
460
|
+
// Apply custom dimensions if set
|
|
461
|
+
if (this.panelWidth > 0) {
|
|
462
|
+
styles.width = `${this.panelWidth}px`;
|
|
463
|
+
}
|
|
464
|
+
if (this.panelHeight > 0) {
|
|
465
|
+
styles.height = `${this.panelHeight}px`;
|
|
466
|
+
}
|
|
467
|
+
// Apply transform for window mode with offsets
|
|
468
|
+
if (this.mode === PanelMode.Window && (this.offsetX !== 0 || this.offsetY !== 0)) {
|
|
469
|
+
styles.transform = `translate(calc(-50% + ${this.offsetX}px), calc(-50% + ${this.offsetY}px))`;
|
|
470
|
+
}
|
|
471
|
+
return styles;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Render header
|
|
475
|
+
*/
|
|
476
|
+
renderHeader() {
|
|
477
|
+
const hasCustomHeader = this.querySelector('[slot="header"]');
|
|
478
|
+
// Header should be draggable in window mode only
|
|
479
|
+
const isDraggable = this.draggable && this.mode === PanelMode.Window;
|
|
480
|
+
return html `
|
|
481
|
+
<div class="${classMap({
|
|
482
|
+
'panel-header': true,
|
|
483
|
+
'panel-header--draggable': isDraggable
|
|
484
|
+
})}"
|
|
485
|
+
@click="${this.mode === PanelMode.Minimized ? this.maximize : nothing}">
|
|
486
|
+
${hasCustomHeader ? html `
|
|
487
|
+
<div class="panel-header-content">
|
|
488
|
+
<slot name="header"></slot>
|
|
489
|
+
</div>
|
|
490
|
+
` : html `
|
|
491
|
+
<div class="panel-header-content">
|
|
492
|
+
${this.icon ? html `
|
|
493
|
+
<nr-icon class="panel-header-icon" name="${this.icon}"></nr-icon>
|
|
494
|
+
` : nothing}
|
|
495
|
+
${this.title ? html `
|
|
496
|
+
<nr-label
|
|
497
|
+
class="panel-title"
|
|
498
|
+
size="${this.getLabelSize()}"
|
|
499
|
+
>${this.title}</nr-label>
|
|
500
|
+
` : nothing}
|
|
501
|
+
</div>
|
|
502
|
+
`}
|
|
503
|
+
|
|
504
|
+
<div class="panel-actions">
|
|
505
|
+
${this.collapsible ? html `
|
|
506
|
+
<button
|
|
507
|
+
class="panel-action-button"
|
|
508
|
+
@click="${this.toggleCollapse}"
|
|
509
|
+
title="${this.collapsed ? 'Expand' : 'Collapse'}">
|
|
510
|
+
<nr-icon name="${this.collapsed ? 'chevron-down' : 'chevron-up'}"></nr-icon>
|
|
511
|
+
</button>
|
|
512
|
+
` : nothing}
|
|
513
|
+
|
|
514
|
+
${this.mode === PanelMode.Window && this.minimizable && !this.isMaximizedFromEmbedded && !this.isTabPopOut ? html `
|
|
515
|
+
<button
|
|
516
|
+
class="panel-action-button"
|
|
517
|
+
@click="${this.minimize}"
|
|
518
|
+
title="Minimize">
|
|
519
|
+
<nr-icon name="minus"></nr-icon>
|
|
520
|
+
</button>
|
|
521
|
+
` : nothing}
|
|
522
|
+
|
|
523
|
+
${this.isTabPopOut && this.mode === PanelMode.Window ? html `
|
|
524
|
+
<button
|
|
525
|
+
class="panel-action-button"
|
|
526
|
+
@click="${this.restore}"
|
|
527
|
+
title="Restore to tabs">
|
|
528
|
+
<nr-icon name="minimize"></nr-icon>
|
|
529
|
+
</button>
|
|
530
|
+
` : nothing}
|
|
531
|
+
|
|
532
|
+
${this.mode === PanelMode.Embedded ? html `
|
|
533
|
+
<button
|
|
534
|
+
class="panel-action-button"
|
|
535
|
+
@click="${this.maximizeEmbedded}"
|
|
536
|
+
title="Maximize to window">
|
|
537
|
+
<nr-icon name="maximize"></nr-icon>
|
|
538
|
+
</button>
|
|
539
|
+
` : nothing}
|
|
540
|
+
|
|
541
|
+
${this.isMaximizedFromEmbedded ? html `
|
|
542
|
+
<button
|
|
543
|
+
class="panel-action-button"
|
|
544
|
+
@click="${this.restoreEmbedded}"
|
|
545
|
+
title="Restore to embedded">
|
|
546
|
+
<nr-icon name="minimize"></nr-icon>
|
|
547
|
+
</button>
|
|
548
|
+
` : nothing}
|
|
549
|
+
|
|
550
|
+
${this.mode === PanelMode.Panel ? html `
|
|
551
|
+
<button
|
|
552
|
+
class="panel-action-button"
|
|
553
|
+
@click="${this.transformToWindow}"
|
|
554
|
+
title="Pop out to window">
|
|
555
|
+
<nr-icon name="external-link"></nr-icon>
|
|
556
|
+
</button>
|
|
557
|
+
` : nothing}
|
|
558
|
+
|
|
559
|
+
${this.mode === PanelMode.Window && !this.isMaximizedFromEmbedded ? html `
|
|
560
|
+
<button
|
|
561
|
+
class="panel-action-button"
|
|
562
|
+
@click="${this.transformToPanel}"
|
|
563
|
+
title="Dock to panel">
|
|
564
|
+
<nr-icon name="layout-sidebar"></nr-icon>
|
|
565
|
+
</button>
|
|
566
|
+
` : nothing}
|
|
567
|
+
|
|
568
|
+
${this.closable ? html `
|
|
569
|
+
<button
|
|
570
|
+
class="panel-action-button"
|
|
571
|
+
@click="${this.close}"
|
|
572
|
+
title="Close">
|
|
573
|
+
<nr-icon name="x"></nr-icon>
|
|
574
|
+
</button>
|
|
575
|
+
` : nothing}
|
|
576
|
+
</div>
|
|
577
|
+
</div>
|
|
578
|
+
`;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Render footer
|
|
582
|
+
*/
|
|
583
|
+
renderFooter() {
|
|
584
|
+
const hasFooter = this.querySelector('[slot="footer"]');
|
|
585
|
+
if (!hasFooter)
|
|
586
|
+
return nothing;
|
|
587
|
+
return html `
|
|
588
|
+
<div class="panel-footer">
|
|
589
|
+
<slot name="footer"></slot>
|
|
590
|
+
</div>
|
|
591
|
+
`;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Render resize handles
|
|
595
|
+
*/
|
|
596
|
+
renderResizeHandles() {
|
|
597
|
+
if (!this.resizable || (this.mode !== PanelMode.Window && this.mode !== PanelMode.Embedded))
|
|
598
|
+
return nothing;
|
|
599
|
+
return html `
|
|
600
|
+
<div class="resize-handle resize-handle-n"></div>
|
|
601
|
+
<div class="resize-handle resize-handle-s"></div>
|
|
602
|
+
<div class="resize-handle resize-handle-e"></div>
|
|
603
|
+
<div class="resize-handle resize-handle-w"></div>
|
|
604
|
+
<div class="resize-handle resize-handle-ne"></div>
|
|
605
|
+
<div class="resize-handle resize-handle-nw"></div>
|
|
606
|
+
<div class="resize-handle resize-handle-se"></div>
|
|
607
|
+
<div class="resize-handle resize-handle-sw"></div>
|
|
608
|
+
`;
|
|
609
|
+
}
|
|
610
|
+
render() {
|
|
611
|
+
if (!this.open)
|
|
612
|
+
return nothing;
|
|
613
|
+
return html `
|
|
614
|
+
<div
|
|
615
|
+
class=${classMap(this.getPanelClasses())}
|
|
616
|
+
style=${styleMap(this.getPanelStyles())}
|
|
617
|
+
data-theme="${this.currentTheme}">
|
|
618
|
+
|
|
619
|
+
${this.renderHeader()}
|
|
620
|
+
|
|
621
|
+
<div class="panel-body">
|
|
622
|
+
<slot></slot>
|
|
623
|
+
</div>
|
|
624
|
+
|
|
625
|
+
${this.renderFooter()}
|
|
626
|
+
${this.renderResizeHandles()}
|
|
627
|
+
</div>
|
|
628
|
+
`;
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
NrPanelElement.styles = styles;
|
|
632
|
+
__decorate([
|
|
633
|
+
property({ type: String })
|
|
634
|
+
], NrPanelElement.prototype, "mode", void 0);
|
|
635
|
+
__decorate([
|
|
636
|
+
property({ type: String })
|
|
637
|
+
], NrPanelElement.prototype, "size", void 0);
|
|
638
|
+
__decorate([
|
|
639
|
+
property({ type: String })
|
|
640
|
+
], NrPanelElement.prototype, "position", void 0);
|
|
641
|
+
__decorate([
|
|
642
|
+
property({ type: String })
|
|
643
|
+
], NrPanelElement.prototype, "maximizePosition", void 0);
|
|
644
|
+
__decorate([
|
|
645
|
+
property({ type: Boolean })
|
|
646
|
+
], NrPanelElement.prototype, "draggable", void 0);
|
|
647
|
+
__decorate([
|
|
648
|
+
property({ type: Boolean })
|
|
649
|
+
], NrPanelElement.prototype, "resizable", void 0);
|
|
650
|
+
__decorate([
|
|
651
|
+
property({ type: Boolean })
|
|
652
|
+
], NrPanelElement.prototype, "collapsible", void 0);
|
|
653
|
+
__decorate([
|
|
654
|
+
property({ type: Boolean })
|
|
655
|
+
], NrPanelElement.prototype, "minimizable", void 0);
|
|
656
|
+
__decorate([
|
|
657
|
+
property({ type: Boolean })
|
|
658
|
+
], NrPanelElement.prototype, "closable", void 0);
|
|
659
|
+
__decorate([
|
|
660
|
+
property({ type: Boolean })
|
|
661
|
+
], NrPanelElement.prototype, "animated", void 0);
|
|
662
|
+
__decorate([
|
|
663
|
+
property({ type: String })
|
|
664
|
+
], NrPanelElement.prototype, "title", void 0);
|
|
665
|
+
__decorate([
|
|
666
|
+
property({ type: String })
|
|
667
|
+
], NrPanelElement.prototype, "icon", void 0);
|
|
668
|
+
__decorate([
|
|
669
|
+
property({ type: String })
|
|
670
|
+
], NrPanelElement.prototype, "width", void 0);
|
|
671
|
+
__decorate([
|
|
672
|
+
property({ type: String })
|
|
673
|
+
], NrPanelElement.prototype, "height", void 0);
|
|
674
|
+
__decorate([
|
|
675
|
+
property({ type: Boolean, reflect: true })
|
|
676
|
+
], NrPanelElement.prototype, "open", void 0);
|
|
677
|
+
__decorate([
|
|
678
|
+
state()
|
|
679
|
+
], NrPanelElement.prototype, "collapsed", void 0);
|
|
680
|
+
__decorate([
|
|
681
|
+
state()
|
|
682
|
+
], NrPanelElement.prototype, "isDragging", void 0);
|
|
683
|
+
__decorate([
|
|
684
|
+
state()
|
|
685
|
+
], NrPanelElement.prototype, "animating", void 0);
|
|
686
|
+
__decorate([
|
|
687
|
+
property({ type: Number })
|
|
688
|
+
], NrPanelElement.prototype, "offsetX", void 0);
|
|
689
|
+
__decorate([
|
|
690
|
+
property({ type: Number })
|
|
691
|
+
], NrPanelElement.prototype, "offsetY", void 0);
|
|
692
|
+
__decorate([
|
|
693
|
+
property({ type: Number })
|
|
694
|
+
], NrPanelElement.prototype, "panelWidth", void 0);
|
|
695
|
+
__decorate([
|
|
696
|
+
property({ type: Number })
|
|
697
|
+
], NrPanelElement.prototype, "panelHeight", void 0);
|
|
698
|
+
__decorate([
|
|
699
|
+
state()
|
|
700
|
+
], NrPanelElement.prototype, "originalMode", void 0);
|
|
701
|
+
__decorate([
|
|
702
|
+
state()
|
|
703
|
+
], NrPanelElement.prototype, "isMaximizedFromEmbedded", void 0);
|
|
704
|
+
__decorate([
|
|
705
|
+
property({ type: Boolean })
|
|
706
|
+
], NrPanelElement.prototype, "isTabPopOut", void 0);
|
|
707
|
+
__decorate([
|
|
708
|
+
state()
|
|
709
|
+
], NrPanelElement.prototype, "isFirstUpdate", void 0);
|
|
710
|
+
__decorate([
|
|
711
|
+
state()
|
|
712
|
+
], NrPanelElement.prototype, "originalEmbeddedWidth", void 0);
|
|
713
|
+
__decorate([
|
|
714
|
+
state()
|
|
715
|
+
], NrPanelElement.prototype, "originalEmbeddedHeight", void 0);
|
|
716
|
+
NrPanelElement = __decorate([
|
|
717
|
+
customElement('nr-panel')
|
|
718
|
+
], NrPanelElement);
|
|
719
|
+
export { NrPanelElement };
|
|
720
|
+
//# sourceMappingURL=panel.component.js.map
|