@brightspace-ui/core 3.305.0 → 3.306.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.
@@ -11,7 +11,12 @@ export const DIVIDER_HANDLE_SIZE = 30;
11
11
  export const KEYBOARD_STEP = 20; // TO DO: Confirm
12
12
  export const KEYBOARD_STEP_LARGE = 80; // TO DO: Confirm
13
13
 
14
+ const DRAG_THRESHOLD = 3; // Number of pixels to move to count as a drag
15
+ const AUTO_EXPAND_WIDTH_FACTOR = 0.1;
16
+ const AUTO_COLLAPSE_WIDTH_FACTOR = 0.75;
17
+
14
18
  const clampedSize = (size, min, max) => Math.max(min, Math.min(size, max));
19
+ const isRtl = () => document.documentElement.getAttribute('dir') === 'rtl';
15
20
 
16
21
  const ICON_ARROW_COLLAPSE_LEFT = html`
17
22
  <svg width="18" height="18" mirror-in-rtl xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
@@ -56,6 +61,11 @@ class PageDivider extends FocusMixin(PropertyRequiredMixin(LitElement)) {
56
61
  * @type {boolean}
57
62
  */
58
63
  collapsed: { type: Boolean, reflect: true },
64
+ /**
65
+ * Size the panel/drawer occupies while collapsed (the drag starts from here)
66
+ * @type {number}
67
+ */
68
+ collapsedSize: { type: Number, attribute: 'collapsed-size' },
59
69
  /**
60
70
  * Current size of the panel/drawer the divider controls
61
71
  * @type {number}
@@ -195,6 +205,7 @@ class PageDivider extends FocusMixin(PropertyRequiredMixin(LitElement)) {
195
205
  super();
196
206
 
197
207
  this.collapsed = false;
208
+ this.collapsedSize = 0;
198
209
  this.currentSize = 0;
199
210
  this.label = '';
200
211
  this.maxSize = 0;
@@ -243,6 +254,48 @@ class PageDivider extends FocusMixin(PropertyRequiredMixin(LitElement)) {
243
254
 
244
255
  #clickedArrow;
245
256
  #clickedHandle = false;
257
+ #draggedDivider = false;
258
+ #dragStats;
259
+
260
+ #handlePointerMove = (e) => {
261
+ if (!this.#dragStats || e.pointerId !== this.#dragStats.pointerId) return;
262
+ const delta = this.panelType === 'panel' ? (e.clientX - this.#dragStats.startX) : (e.clientY - this.#dragStats.startY);
263
+ if (Math.abs(delta) >= DRAG_THRESHOLD) this.#dragStats.moved = true;
264
+ if (!this.#dragStats.moved) return;
265
+
266
+ let growthDirectionIsPositive;
267
+ if (this.panelType === 'panel') {
268
+ growthDirectionIsPositive = (this.panelPosition === 'start') !== isRtl();
269
+ } else if (this.panelType === 'drawer') {
270
+ growthDirectionIsPositive = false;
271
+ }
272
+ const signedDelta = (growthDirectionIsPositive ? 1 : -1) * delta;
273
+ const requestedSize = this.#dragStats.startSize + signedDelta;
274
+
275
+ this.#dragStats.lastSize = requestedSize;
276
+ this.#sendResizeLiveEvent(requestedSize);
277
+ };
278
+
279
+ #handlePointerUp = (e) => {
280
+ if (!this.#dragStats || e.pointerId !== this.#dragStats.pointerId) return;
281
+ const target = e.currentTarget;
282
+ target.removeEventListener('pointermove', this.#handlePointerMove);
283
+ target.removeEventListener('pointerup', this.#handlePointerUp);
284
+ target.removeEventListener('pointercancel', this.#handlePointerUp);
285
+
286
+ if (this.collapsed && this.#dragStats.lastSize > this.minSize * AUTO_EXPAND_WIDTH_FACTOR) {
287
+ this.#draggedDivider = true;
288
+ this.#sendToggleEvent();
289
+ this.#sendResizeEvent(this.#dragStats.lastSize);
290
+ } else if (!this.collapsed && this.#dragStats.lastSize < this.minSize * AUTO_COLLAPSE_WIDTH_FACTOR) {
291
+ this.#draggedDivider = true;
292
+ this.#sendToggleEvent();
293
+ } else if (this.#dragStats.moved) {
294
+ this.#draggedDivider = true;
295
+ this.#sendResizeEvent(this.#dragStats.lastSize);
296
+ }
297
+ this.#dragStats = null;
298
+ };
246
299
 
247
300
  #getArrowVisibility() {
248
301
  if (this.panelType !== 'panel' || this.collapsed) return { showStartArrow: false, showEndArrow: false };
@@ -269,6 +322,8 @@ class PageDivider extends FocusMixin(PropertyRequiredMixin(LitElement)) {
269
322
  // Do not toggle/resize until click event is received,
270
323
  // to avoid clicking on elements under the arrows in overlay mode or under the handle in drawer mode
271
324
  e.stopPropagation();
325
+ if (this.#draggedDivider) return;
326
+
272
327
  if (this.collapsed || this.#clickedHandle) {
273
328
  this.#sendToggleEvent();
274
329
  } else if (this.#clickedArrow) {
@@ -303,8 +358,7 @@ class PageDivider extends FocusMixin(PropertyRequiredMixin(LitElement)) {
303
358
  let positiveStepKey;
304
359
  if (this.panelType === 'panel') {
305
360
  if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return;
306
- const isRtl = (document.documentElement.getAttribute('dir') === 'rtl');
307
- positiveStepKey = (this.panelPosition === 'start') !== isRtl ? 'ArrowRight' : 'ArrowLeft';
361
+ positiveStepKey = (this.panelPosition === 'start') !== isRtl() ? 'ArrowRight' : 'ArrowLeft';
308
362
  } else if (this.panelType === 'drawer') {
309
363
  if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
310
364
  positiveStepKey = 'ArrowUp';
@@ -316,8 +370,11 @@ class PageDivider extends FocusMixin(PropertyRequiredMixin(LitElement)) {
316
370
  }
317
371
 
318
372
  #handlePointerDown(e) {
373
+ this.#draggedDivider = false;
374
+
319
375
  if (e.button !== 0) return; // Don't collapse when right-clicking to debug
320
376
  e.preventDefault();
377
+ e.stopPropagation();
321
378
  this.focus();
322
379
 
323
380
  const path = e.composedPath();
@@ -325,7 +382,21 @@ class PageDivider extends FocusMixin(PropertyRequiredMixin(LitElement)) {
325
382
  this.#clickedArrow = path.find(el => el.classList?.contains('divider-arrow'));
326
383
  if (this.#clickedArrow) return; // Arrows don't support dragging
327
384
 
328
- // TO DO: Dragging
385
+ const startSize = this.collapsed ? this.collapsedSize : this.currentSize;
386
+ this.#dragStats = {
387
+ pointerId: e.pointerId,
388
+ startX: e.clientX,
389
+ startY: e.clientY,
390
+ startSize,
391
+ lastSize: startSize,
392
+ moved: false
393
+ };
394
+
395
+ const target = e.currentTarget;
396
+ target.setPointerCapture(e.pointerId);
397
+ target.addEventListener('pointermove', this.#handlePointerMove);
398
+ target.addEventListener('pointerup', this.#handlePointerUp);
399
+ target.addEventListener('pointercancel', this.#handlePointerUp);
329
400
  }
330
401
 
331
402
  #sendResizeEvent(requestedSize) {
@@ -336,6 +407,15 @@ class PageDivider extends FocusMixin(PropertyRequiredMixin(LitElement)) {
336
407
  } }));
337
408
  }
338
409
 
410
+ #sendResizeLiveEvent(requestedSize) {
411
+ const clampedRequestedSize = clampedSize(requestedSize, this.collapsedSize, this.maxSize);
412
+ if (clampedRequestedSize === this.currentSize) return; // Don't bother sending events when dragging past min/max
413
+ /** @ignore */
414
+ this.dispatchEvent(new CustomEvent('d2l-page-divider-resize-live', { detail: {
415
+ requestedSize: clampedRequestedSize
416
+ } }));
417
+ }
418
+
339
419
  #sendToggleEvent() {
340
420
  /** @ignore */
341
421
  this.dispatchEvent(new CustomEvent('d2l-page-divider-toggle'));
@@ -37,6 +37,7 @@ class PanelStateController {
37
37
  collapsed: config.collapsed,
38
38
  restoreCollapsed: !config.collapsed,
39
39
  size: 0,
40
+ dragSize: null,
40
41
  minSize: config.minSize,
41
42
  maxSize: config.minSize
42
43
  };
@@ -50,11 +51,14 @@ class PanelStateController {
50
51
  getMinSize(key) { return this.#panels[key].minSize; }
51
52
  getSize(key) {
52
53
  const panel = this.#panels[key];
53
- // TO DO: Factor in dragging
54
+ if (panel.dragSize !== null) return panel.dragSize;
54
55
  return panel.collapsed ? 0 : panel.size;
55
56
  }
56
57
  getTrueSize(key) {
57
58
  const panel = this.#panels[key];
59
+ if (panel.dragSize !== null) {
60
+ return Math.max(panel.minSize, panel.dragSize);
61
+ }
58
62
  return panel.size;
59
63
  }
60
64
 
@@ -78,6 +82,7 @@ class PanelStateController {
78
82
  // Clamp requested size to min and max bounds
79
83
  panel.size = Math.max(panel.minSize, Math.min(requestedSize, panel.maxSize));
80
84
  panel.animate = animate;
85
+ panel.dragSize = null;
81
86
  this.#host.requestUpdate();
82
87
  if (storeState) this.#storePanelState(key);
83
88
  }
@@ -85,14 +90,15 @@ class PanelStateController {
85
90
  setCollapsed(key, collapsed) {
86
91
  const panel = this.#panels[key];
87
92
  panel.collapsed = collapsed;
93
+ panel.dragSize = null;
88
94
  panel.animate = true;
89
95
  this.#host.requestUpdate();
90
96
  this.#storePanelState(key);
91
97
  }
92
98
 
93
- setDragSize(key) {
99
+ setDragSize(key, dragSize) {
94
100
  const panel = this.#panels[key];
95
- // TO DO: Handle Dragging
101
+ panel.dragSize = dragSize;
96
102
  panel.animate = false;
97
103
  this.#host.requestUpdate();
98
104
  }
@@ -539,6 +545,11 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
539
545
  this._panelState.resize(panelKey, e.detail.requestedSize, { animate: true, storeState: true });
540
546
  };
541
547
 
548
+ #handleDividerResizeLive(e) {
549
+ const panelKey = e.target.dataset.panelKey;
550
+ this._panelState.setDragSize(panelKey, e.detail.requestedSize);
551
+ }
552
+
542
553
  #handleDividerToggle(e) {
543
554
  const panelKey = e.target.dataset.panelKey;
544
555
  const collapsed = !this._panelState.getCollapsed(panelKey);
@@ -577,11 +588,13 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
577
588
  data-panel-key="${panelKey}"
578
589
  label="${label}"
579
590
  ?collapsed="${this._panelState.getCollapsed(panelKey)}"
591
+ collapsed-size="${DIVIDER_GUTTER_WIDTH}"
580
592
  current-size="${this._panelState.getSize(panelKey)}"
581
593
  max-size="${this._panelState.getMaxSize(panelKey)}"
582
594
  min-size="${this._panelState.getMinSize(panelKey)}"
583
595
  panel-position="${ifDefined(panelPosition)}"
584
596
  @d2l-page-divider-resize="${this.#handleDividerResize}"
597
+ @d2l-page-divider-resize-live="${this.#handleDividerResizeLive}"
585
598
  @d2l-page-divider-toggle="${this.#handleDividerToggle}"
586
599
  ></d2l-page-divider-internal>
587
600
  `;
@@ -627,7 +640,7 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
627
640
  const classes = {
628
641
  'side-nav-panel': true,
629
642
  'animate': this._panelState.getAnimate(panelKey),
630
- 'collapsed': this._panelState.getCollapsed(panelKey)
643
+ 'collapsed': this._panelState.getSize(panelKey) === 0 // Collapsed and not being dragged
631
644
  };
632
645
  return html`
633
646
  <nav class="side-nav" ?hidden="${!this._slotVisibility['side-nav']}" aria-label="${this.localize('components.page.side-nav-label')}">
@@ -648,7 +661,7 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
648
661
  const classes = {
649
662
  'supporting-panel': true,
650
663
  'animate': this._panelState.getAnimate(panelKey),
651
- 'collapsed': this._panelState.getCollapsed(panelKey)
664
+ 'collapsed': this._panelState.getSize(panelKey) === 0 // Collapsed and not being dragged
652
665
  };
653
666
  return html`
654
667
  <aside class="supporting" ?hidden="${!this._slotVisibility['supporting']}" aria-label="${this.localize('components.page.supporting-label')}">
@@ -13254,6 +13254,12 @@
13254
13254
  "type": "boolean",
13255
13255
  "default": "false"
13256
13256
  },
13257
+ {
13258
+ "name": "collapsed-size",
13259
+ "description": "Size the panel/drawer occupies while collapsed (the drag starts from here)",
13260
+ "type": "number",
13261
+ "default": "0"
13262
+ },
13257
13263
  {
13258
13264
  "name": "current-size",
13259
13265
  "description": "Current size of the panel/drawer the divider controls",
@@ -13308,6 +13314,13 @@
13308
13314
  "type": "boolean",
13309
13315
  "default": "false"
13310
13316
  },
13317
+ {
13318
+ "name": "collapsedSize",
13319
+ "attribute": "collapsed-size",
13320
+ "description": "Size the panel/drawer occupies while collapsed (the drag starts from here)",
13321
+ "type": "number",
13322
+ "default": "0"
13323
+ },
13311
13324
  {
13312
13325
  "name": "currentSize",
13313
13326
  "attribute": "current-size",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.305.0",
3
+ "version": "3.306.0",
4
4
  "description": "A collection of accessible, free, open-source web components for building Brightspace applications",
5
5
  "type": "module",
6
6
  "repository": "https://github.com/BrightspaceUI/core.git",