@brightspace-ui/core 3.280.4 → 3.282.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.
@@ -127,12 +127,12 @@ class PageDivider extends FocusMixin(PropertyRequiredMixin(LitElement)) {
127
127
  width: ${DIVIDER_HANDLE_SIZE}px;
128
128
  }
129
129
  .divider-handle:hover {
130
- background-color: var(--d2l-color-sylvite);
130
+ background-color: var(--d2l-color-gypsum);
131
131
  border-color: var(--d2l-color-celestine);
132
132
  }
133
133
  .slider:focus .divider-handle {
134
- background-color: var(--d2l-color-celestine);
135
- border-color: var(--d2l-color-celestine);
134
+ background-color: var(--d2l-color-celestine-minus-1);
135
+ border-color: var(--d2l-color-celestine-minus-1);
136
136
  }
137
137
  .slider:focus .divider-handle .handle-icon {
138
138
  color: white;
@@ -7,6 +7,8 @@ import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
7
7
  import { ProviderMixin } from '../../mixins/provider/provider-mixin.js';
8
8
  import { styleMap } from 'lit/directives/style-map.js';
9
9
 
10
+ export const panelStateStorageKey = key => `d2l-page-panel-state-${key}`;
11
+
10
12
  const DRAWER_MIN_HEIGHT = 200; // TO DO: Confirm
11
13
  const MAIN_MIN_WIDTH = 600; // TO DO: Confirm
12
14
  const PANEL_MIN_WIDTH = 320;
@@ -41,12 +43,28 @@ class PanelStateController {
41
43
  return panel.size;
42
44
  }
43
45
 
44
- resize(key, requestedSize, animate = false) {
46
+ initialize(defaults) {
47
+ const storedState = this.#getStoredState();
48
+
49
+ for (const [key, panel] of Object.entries(this.#panels)) {
50
+ const stored = storedState?.[key];
51
+ if (stored) {
52
+ const storedSize = parseInt(stored.size);
53
+ this.resize(key, isFinite(storedSize) ? storedSize : defaults[key]);
54
+ if (stored.collapsed) panel.collapsed = true;
55
+ } else {
56
+ this.resize(key, defaults[key]);
57
+ }
58
+ }
59
+ }
60
+
61
+ resize(key, requestedSize, { animate = false, storeState = false } = {}) {
45
62
  const panel = this.#panels[key];
46
63
  // Clamp requested size to min and max bounds
47
64
  panel.size = Math.max(panel.minSize, Math.min(requestedSize, panel.maxSize));
48
65
  panel.animate = animate;
49
66
  this.#host.requestUpdate();
67
+ if (storeState) this.#storePanelState(key);
50
68
  }
51
69
 
52
70
  setCollapsed(key, collapsed) {
@@ -54,6 +72,7 @@ class PanelStateController {
54
72
  panel.collapsed = collapsed;
55
73
  panel.animate = true;
56
74
  this.#host.requestUpdate();
75
+ this.#storePanelState(key);
57
76
  }
58
77
 
59
78
  setDragSize(key) {
@@ -74,6 +93,39 @@ class PanelStateController {
74
93
 
75
94
  #host;
76
95
  #panels;
96
+
97
+ #getPanelStateStorageKey() {
98
+ return this.#host.stateStorageKey ? panelStateStorageKey(this.#host.stateStorageKey) : null;
99
+ }
100
+
101
+ #getStoredState() {
102
+ const fullKey = this.#getPanelStateStorageKey();
103
+ if (!fullKey) return;
104
+
105
+ try {
106
+ return JSON.parse(localStorage.getItem(fullKey));
107
+ } catch {
108
+ // Return nothing if local storage isn't available or has bad data
109
+ return;
110
+ }
111
+ }
112
+
113
+ #storePanelState(panelKey) {
114
+ const fullKey = this.#getPanelStateStorageKey();
115
+ if (!fullKey) return;
116
+ const state = this.#getStoredState() || {};
117
+
118
+ try {
119
+ state[panelKey] = {
120
+ size: this.#panels[panelKey].size,
121
+ collapsed: this.#panels[panelKey].collapsed
122
+ };
123
+ localStorage.setItem(fullKey, JSON.stringify(state));
124
+ } catch {
125
+ // Do nothing if storage quota exceeded or using private browsing mode of an old Safari version
126
+ // TO DO: If QuotaExceededError, log this to our logging framework
127
+ }
128
+ }
77
129
  }
78
130
 
79
131
  /**
@@ -87,6 +139,12 @@ class PanelStateController {
87
139
  class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
88
140
 
89
141
  static properties = {
142
+ /**
143
+ * Key used to optionally store page state across reloads (using localStorage). Different pages should use different keys.
144
+ * Currently, this includes panel state info.
145
+ * @type {string}
146
+ */
147
+ stateStorageKey: { type: String, attribute: 'state-storage-key' },
90
148
  /**
91
149
  * Width type of the page and its underlying pieces
92
150
  * @type {'normal'|'wide'|'fullscreen'}
@@ -354,7 +412,7 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
354
412
 
355
413
  #handleDividerResize(e) {
356
414
  const panelKey = e.target.dataset.panelKey;
357
- this._panelState.resize(panelKey, e.detail.requestedSize, true);
415
+ this._panelState.resize(panelKey, e.detail.requestedSize, { animate: true, storeState: true });
358
416
  };
359
417
 
360
418
  #handleDividerToggle(e) {
@@ -373,9 +431,11 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
373
431
  const defaultWidth = Math.floor(this._contentWidth / 3);
374
432
  const defaultHeight = Math.floor(window.innerHeight / 2);
375
433
 
376
- this._panelState.resize('side-nav', defaultWidth);
377
- this._panelState.resize('supporting', defaultWidth);
378
- this._panelState.resize('supporting-mobile', defaultHeight);
434
+ this._panelState.initialize({
435
+ 'side-nav': defaultWidth,
436
+ 'supporting': defaultWidth,
437
+ 'supporting-mobile': defaultHeight
438
+ });
379
439
  }
380
440
 
381
441
  #renderDivider(panelKey, label, panelPosition) {
@@ -13202,7 +13202,7 @@
13202
13202
  "properties": [
13203
13203
  {
13204
13204
  "name": "styles",
13205
- "default": "\"css`\\n\\t\\t:host {\\n\\t\\t\\tdisplay: inline-block;\\n\\t\\t\\tflex: none;\\n\\t\\t}\\n\\t\\t.divider {\\n\\t\\t\\tbackground-color: var(--d2l-color-mica);\\n\\t\\t\\tcursor: ew-resize;\\n\\t\\t\\theight: 100%;\\n\\t\\t\\tposition: relative;\\n\\t\\t\\twidth: ${DIVIDER_WIDTH}px;\\n\\t\\t}\\n\\t\\t.divider:hover {\\n\\t\\t\\tbackground-color: var(--d2l-color-corundum);\\n\\t\\t}\\n\\t\\t.divider:focus-within {\\n\\t\\t\\tbackground-color: var(--d2l-color-celestine);\\n\\t\\t}\\n\\n\\t\\t.slider {\\n\\t\\t\\tinset-inline-start: -${DIVIDER_HANDLE_SIZE / 2 - DIVIDER_WIDTH / 2}px;\\n\\t\\t\\toutline: none;\\n\\t\\t\\tposition: absolute;\\n\\t\\t\\ttop: 55px;\\n\\t\\t}\\n\\n\\t\\t.divider-handle {\\n\\t\\t\\talign-items: center;\\n\\t\\t\\tbackground-color: white;\\n\\t\\t\\tborder: 1px solid var(--d2l-color-mica);\\n\\t\\t\\tborder-radius: 50%;\\n\\t\\t\\tbox-sizing: border-box;\\n\\t\\t\\tcursor: pointer;\\n\\t\\t\\tdisplay: flex;\\n\\t\\t\\theight: ${DIVIDER_HANDLE_SIZE}px;\\n\\t\\t\\tjustify-content: center;\\n\\t\\t\\twidth: ${DIVIDER_HANDLE_SIZE}px;\\n\\t\\t}\\n\\t\\t.divider-handle:hover {\\n\\t\\t\\tbackground-color: var(--d2l-color-sylvite);\\n\\t\\t\\tborder-color: var(--d2l-color-celestine);\\n\\t\\t}\\n\\t\\t.slider:focus .divider-handle {\\n\\t\\t\\tbackground-color: var(--d2l-color-celestine);\\n\\t\\t\\tborder-color: var(--d2l-color-celestine);\\n\\t\\t}\\n\\t\\t.slider:focus .divider-handle .handle-icon {\\n\\t\\t\\tcolor: white;\\n\\t\\t}\\n\\n\\t\\t:host([panel-type=\\\"drawer\\\"]) .divider {\\n\\t\\t\\tbackground-color: var(--d2l-color-celestine);\\n\\t\\t\\tcursor: ns-resize;\\n\\t\\t\\theight: ${DIVIDER_WIDTH}px;\\n\\t\\t\\twidth: 100%;\\n\\t\\t}\\n\\n\\t\\t:host([panel-type=\\\"drawer\\\"]) .slider {\\n\\t\\t\\tinset-inline-end: 18px;\\n\\t\\t\\ttop: auto;\\n\\t\\t}\\n\\n\\t\\t/* TO DO: Lots more divider styling to come */\\n\\n\\t`\""
13205
+ "default": "\"css`\\n\\t\\t:host {\\n\\t\\t\\tdisplay: inline-block;\\n\\t\\t\\tflex: none;\\n\\t\\t}\\n\\t\\t.divider {\\n\\t\\t\\tbackground-color: var(--d2l-color-mica);\\n\\t\\t\\tcursor: ew-resize;\\n\\t\\t\\theight: 100%;\\n\\t\\t\\tposition: relative;\\n\\t\\t\\twidth: ${DIVIDER_WIDTH}px;\\n\\t\\t}\\n\\t\\t.divider:hover {\\n\\t\\t\\tbackground-color: var(--d2l-color-corundum);\\n\\t\\t}\\n\\t\\t.divider:focus-within {\\n\\t\\t\\tbackground-color: var(--d2l-color-celestine);\\n\\t\\t}\\n\\n\\t\\t.slider {\\n\\t\\t\\tinset-inline-start: -${DIVIDER_HANDLE_SIZE / 2 - DIVIDER_WIDTH / 2}px;\\n\\t\\t\\toutline: none;\\n\\t\\t\\tposition: absolute;\\n\\t\\t\\ttop: 55px;\\n\\t\\t}\\n\\n\\t\\t.divider-handle {\\n\\t\\t\\talign-items: center;\\n\\t\\t\\tbackground-color: white;\\n\\t\\t\\tborder: 1px solid var(--d2l-color-mica);\\n\\t\\t\\tborder-radius: 50%;\\n\\t\\t\\tbox-sizing: border-box;\\n\\t\\t\\tcursor: pointer;\\n\\t\\t\\tdisplay: flex;\\n\\t\\t\\theight: ${DIVIDER_HANDLE_SIZE}px;\\n\\t\\t\\tjustify-content: center;\\n\\t\\t\\twidth: ${DIVIDER_HANDLE_SIZE}px;\\n\\t\\t}\\n\\t\\t.divider-handle:hover {\\n\\t\\t\\tbackground-color: var(--d2l-color-gypsum);\\n\\t\\t\\tborder-color: var(--d2l-color-celestine);\\n\\t\\t}\\n\\t\\t.slider:focus .divider-handle {\\n\\t\\t\\tbackground-color: var(--d2l-color-celestine-minus-1);\\n\\t\\t\\tborder-color: var(--d2l-color-celestine-minus-1);\\n\\t\\t}\\n\\t\\t.slider:focus .divider-handle .handle-icon {\\n\\t\\t\\tcolor: white;\\n\\t\\t}\\n\\n\\t\\t:host([panel-type=\\\"drawer\\\"]) .divider {\\n\\t\\t\\tbackground-color: var(--d2l-color-celestine);\\n\\t\\t\\tcursor: ns-resize;\\n\\t\\t\\theight: ${DIVIDER_WIDTH}px;\\n\\t\\t\\twidth: 100%;\\n\\t\\t}\\n\\n\\t\\t:host([panel-type=\\\"drawer\\\"]) .slider {\\n\\t\\t\\tinset-inline-end: 18px;\\n\\t\\t\\ttop: auto;\\n\\t\\t}\\n\\n\\t\\t/* TO DO: Lots more divider styling to come */\\n\\n\\t`\""
13206
13206
  },
13207
13207
  {
13208
13208
  "name": "focusElementSelector",
@@ -13574,6 +13574,11 @@
13574
13574
  "path": "./components/page/page.js",
13575
13575
  "description": "Component for laying out a page, with header, optional footer and optional navigation or supporting panels",
13576
13576
  "attributes": [
13577
+ {
13578
+ "name": "state-storage-key",
13579
+ "description": "Key used to optionally store page state across reloads (using localStorage). Different pages should use different keys.\nCurrently, this includes panel state info.",
13580
+ "type": "string"
13581
+ },
13577
13582
  {
13578
13583
  "name": "width-type",
13579
13584
  "description": "Width type of the page and its underlying pieces",
@@ -13582,6 +13587,12 @@
13582
13587
  }
13583
13588
  ],
13584
13589
  "properties": [
13590
+ {
13591
+ "name": "stateStorageKey",
13592
+ "attribute": "state-storage-key",
13593
+ "description": "Key used to optionally store page state across reloads (using localStorage). Different pages should use different keys.\nCurrently, this includes panel state info.",
13594
+ "type": "string"
13595
+ },
13585
13596
  {
13586
13597
  "name": "styles",
13587
13598
  "default": "\"css`\\n\\t\\t:host {\\n\\t\\t\\t--d2l-page-header-max-width: 1230px;\\n\\t\\t\\t--d2l-page-content-max-width: 1230px;\\n\\t\\t\\t--d2l-page-footer-max-width: 1230px;\\n\\t\\t\\t--d2l-page-margin-inline: auto;\\n\\t\\t\\t--d2l-page-padding: 30px;\\n\\t\\t}\\n\\n\\t\\t:host([width-type=\\\"wide\\\"]) {\\n\\t\\t\\t--d2l-page-header-max-width: 1440px;\\n\\t\\t\\t--d2l-page-content-max-width: 1440px;\\n\\t\\t\\t--d2l-page-footer-max-width: 1440px;\\n\\t\\t}\\n\\n\\t\\t:host([width-type=\\\"fullscreen\\\"]) {\\n\\t\\t\\t--d2l-page-header-max-width: 100%;\\n\\t\\t\\t--d2l-page-content-max-width: 100%;\\n\\t\\t\\t--d2l-page-footer-max-width: 100%;\\n\\t\\t}\\n\\n\\t\\t@media (max-width: 929px) {\\n\\t\\t\\t:host {\\n\\t\\t\\t\\t--d2l-page-padding: 24px;\\n\\t\\t\\t}\\n\\t\\t}\\n\\t\\t@media (max-width: 767px) {\\n\\t\\t\\t:host {\\n\\t\\t\\t\\t--d2l-page-padding: 18px;\\n\\t\\t\\t}\\n\\t\\t}\\n\\n\\t\\t.header {\\n\\t\\t\\tposition: relative;\\n\\t\\t\\tz-index: 16; /* To be over sticky content of our core components and the divider */\\n\\t\\t}\\n\\n\\t\\t.page.header-sticky .header {\\n\\t\\t\\tposition: sticky;\\n\\t\\t\\ttop: 0;\\n\\t\\t}\\n\\n\\t\\t.content {\\n\\t\\t\\tbox-sizing: border-box;\\n\\t\\t\\tdisplay: flex;\\n\\t\\t\\tmargin-inline: var(--d2l-page-margin-inline, 0);\\n\\t\\t\\tmax-width: var(--d2l-page-content-max-width, 100%);\\n\\t\\t\\tpadding-bottom: var(--d2l-page-footer-height, 0); /* Reserve space for fixed footer */\\n\\t\\t}\\n\\t\\t.content.has-panels {\\n\\t\\t\\tmin-height: calc(100vh - var(--d2l-page-header-height-measured, 0px));\\n\\t\\t}\\n\\n\\t\\tmain {\\n\\t\\t\\tflex: 1;\\n\\t\\t\\tmin-width: min(${MAIN_MIN_WIDTH}px, 100%);\\n\\t\\t}\\n\\n\\t\\t.side-nav,\\n\\t\\t.supporting {\\n\\t\\t\\tdisplay: contents;\\n\\t\\t}\\n\\n\\t\\t.side-nav-panel,\\n\\t\\t.supporting-panel {\\n\\t\\t\\toverflow: hidden;\\n\\t\\t}\\n\\n\\t\\t.side-nav-panel,\\n\\t\\t.supporting-panel,\\n\\t\\t.divider {\\n\\t\\t\\tposition: sticky;\\n\\t\\t\\ttop: var(--d2l-page-header-height, 0);\\n\\t\\t}\\n\\n\\t\\t.side-nav-panel,\\n\\t\\t.supporting-panel,\\n\\t\\t.side-nav-panel-content,\\n\\t\\t.supporting-panel-content,\\n\\t\\t.divider {\\n\\t\\t\\tmax-height: calc(100vh - var(--d2l-page-header-height, 0) - var(--d2l-page-footer-height, 0));\\n\\t\\t}\\n\\n\\t\\t.side-nav-panel-content,\\n\\t\\t.supporting-panel-content {\\n\\t\\t\\tbox-sizing: border-box;\\n\\t\\t\\toverflow: clip auto;\\n\\t\\t}\\n\\t\\t.side-nav-panel-content {\\n\\t\\t\\tfloat: inline-end;\\n\\t\\t}\\n\\t\\t.supporting-panel-content {\\n\\t\\t\\tfloat: inline-start;\\n\\t\\t}\\n\\n\\t\\t.divider {\\n\\t\\t\\tz-index: 15; /* To be over d2l-page-* panel headers */\\n\\t\\t}\\n\\n\\t\\t.side-nav .divider[collapsed] {\\n\\t\\t\\tmargin-inline-start: 18px;\\n\\t\\t}\\n\\t\\t.supporting .divider[collapsed] {\\n\\t\\t\\tmargin-inline-end: 18px;\\n\\t\\t}\\n\\t\\t.side-nav-panel.collapsed,\\n\\t\\t.supporting-panel.collapsed {\\n\\t\\t\\tvisibility: hidden;\\n\\t\\t}\\n\\t\\t@media (prefers-reduced-motion: no-preference) {\\n\\t\\t\\t.side-nav-panel.animate,\\n\\t\\t\\t.supporting-panel.animate,\\n\\t\\t\\t.side-nav-panel.animate .side-nav-panel-content,\\n\\t\\t\\t.supporting-panel.animate .supporting-panel-content {\\n\\t\\t\\t\\ttransition: width 400ms cubic-bezier(0, 0.7, 0.5, 1);\\n\\t\\t\\t}\\n\\t\\t\\t.divider.animate {\\n\\t\\t\\t\\ttransition: margin 400ms cubic-bezier(0, 0.7, 0.5, 1);\\n\\t\\t\\t}\\n\\t\\t\\t.side-nav-panel.animate.collapsed,\\n\\t\\t\\t.supporting-panel.animate.collapsed {\\n\\t\\t\\t\\ttransition: width 400ms cubic-bezier(0, 0.7, 0.5, 1), visibility 0s 400ms;\\n\\t\\t\\t}\\n\\t\\t}\\n\\n\\t\\t.footer:not([hidden]),\\n\\t\\t.floating-buttons-container {\\n\\t\\t\\tdisplay: inline;\\n\\t\\t}\\n\\t\\t.fixed-footer {\\n\\t\\t\\tbackground-color: white;\\n\\t\\t\\tbox-shadow: 0 -2px 4px rgba(32, 33, 34, 0.2); /* ferrite */\\n\\t\\t\\tinset: auto 0 0;\\n\\t\\t\\tpadding-block-start: 0.75rem;\\n\\t\\t\\tposition: fixed;\\n\\t\\t\\tz-index: 15; /* To be over sticky content of our core components and divider */\\n\\t\\t}\\n\\t\\t.footer-contents {\\n\\t\\t\\tmargin-inline: var(--d2l-page-margin-inline, 0);\\n\\t\\t\\tmax-width: var(--d2l-page-footer-max-width, 100%);\\n\\t\\t}\\n\\t`\""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.280.4",
3
+ "version": "3.282.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",
@@ -57,7 +57,7 @@
57
57
  "@rollup/plugin-replace": "^6",
58
58
  "@stylistic/eslint-plugin": "^5",
59
59
  "@web/dev-server": "^1.0",
60
- "chalk": "^5",
60
+ "chalk": "^6",
61
61
  "eslint": "^9",
62
62
  "eslint-config-brightspace": "^4",
63
63
  "eslint-plugin-unicorn": "^65",