@brightspace-ui/core 3.8.0 → 3.9.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.
@@ -0,0 +1,137 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
6
+ <meta charset="UTF-8">
7
+ <link rel="stylesheet" href="../../demo/styles.css" type="text/css">
8
+ <script>
9
+ const urlParams = new URLSearchParams(window.location.search);
10
+ window.D2L = { LP: { Web: { UI: { Flags: { Flag: () => (urlParams.get('position') === 'fixed') } } } } };
11
+ </script>
12
+ <script type="module">
13
+ import '../../demo/demo-page.js';
14
+ import '../../button/button.js';
15
+ import '../../button/button-subtle.js';
16
+ import '../../dialog/dialog.js';
17
+ import '../dropdown.js';
18
+ import '../dropdown-content.js';
19
+ import '../../table/demo/table-test.js';
20
+ </script>
21
+ </head>
22
+
23
+ <body unresolved>
24
+
25
+ <d2l-demo-page page-title="d2l-dropdown">
26
+
27
+ <h2>Dropdown (in a scrollable container)</h2>
28
+ <d2l-demo-snippet>
29
+ <template>
30
+ <div style="height: 250px; overflow: scroll;">
31
+ <p>Gabion warp American Main gunwalls cutlass gally cable gibbet jib keel. Trysail chantey swing the lead hempen halter hang the jib chase Jack Tar furl galleon scurvy. Brig splice the main brace provost pink rutters tender heave to Shiver me timbers belaying pin Brethren of the Coast.</p>
32
+ <d2l-dropdown prefer-fixed-positioning>
33
+ <d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
34
+ <d2l-dropdown-content max-width="400" prefer-fixed-positioning>
35
+ <a href="https://youtu.be/9ze87zQFSak">Google</a>
36
+ <p>Shrouds hulk ye run a rig pink wherry hornswaggle overhaul spike splice the main brace. Barbary Coast salmagundi Nelsons folly lanyard Sea Legs topgallant Sink me crow's nest scuttle red ensign. Handsomely swab wench hang the jib square-rigged scuppers spyglass holystone Yellow Jack splice the main brace.</p>
37
+ <a href="http://www.desire2learn.com">D2L</a>
38
+ </d2l-dropdown-content>
39
+ </d2l-dropdown>
40
+ <p>Shiver me timbers to go on account lookout wherry doubloon chase. Belay yo-ho-ho keelhaul squiffy black spot yardarm spyglass sheet transom heave to.</p>
41
+ <p>Trysail Sail ho Corsair red ensign hulk smartly boom jib rum gangway. Case shot Shiver me timbers gangplank crack Jennys tea cup ballast Blimey lee snow crow's nest rutters. Fluke jib scourge of the seven seas boatswain schooner gaff booty Jack Tar transom spirits.</p>
42
+ <p>Hardtack hang the jib haul wind booty pillage spike hearties Pirate Round tack yard. Piracy fire ship trysail stern scurvy blow the man down skysail salmagundi lee grog blossom. Hands gabion ho schooner lad ballast keel mutiny square-rigged haul wind.</p>
43
+ </div>
44
+ </template>
45
+ </d2l-demo-snippet>
46
+
47
+ <h2>Dropdown (in a table with sticky headers)</h2>
48
+
49
+ <d2l-demo-snippet>
50
+ <template>
51
+ <d2l-test-table type="default" sticky-headers sticky-controls></d2l-test-table>
52
+ </template>
53
+ </d2l-demo-snippet>
54
+
55
+ <h2>Dropdown (in another dropdown)</h2>
56
+
57
+ <d2l-demo-snippet>
58
+ <template>
59
+ <d2l-dropdown prefer-fixed-positioning>
60
+ <d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
61
+ <d2l-dropdown-content max-width="400" prefer-fixed-positioning>
62
+ <p>Shrouds hulk ye run a rig pink wherry hornswaggle overhaul spike splice the main brace. Barbary Coast salmagundi Nelsons folly lanyard Sea Legs topgallant Sink me crow's nest scuttle red ensign.</p>
63
+ <d2l-dropdown prefer-fixed-positioning>
64
+ <d2l-button-subtle class="d2l-dropdown-opener">Open Nested!</d2l-button-subtle>
65
+ <d2l-dropdown-content max-width="600" align="start" prefer-fixed-positioning>
66
+ <a href="https://youtu.be/9ze87zQFSak">Google</a>
67
+ <p>Shrouds hulk ye run a rig pink wherry hornswaggle overhaul spike splice the main brace. Barbary Coast salmagundi Nelsons folly lanyard Sea Legs topgallant Sink me crow's nest scuttle red ensign. Handsomely swab wench hang the jib square-rigged scuppers spyglass holystone Yellow Jack splice the main brace.</p>
68
+ <a href="http://www.desire2learn.com">D2L</a>
69
+ </d2l-dropdown-content>
70
+ </d2l-dropdown>
71
+ <p>Handsomely swab wench hang the jib square-rigged scuppers spyglass holystone Yellow Jack splice the main brace.</p>
72
+ </d2l-dropdown-content>
73
+ </d2l-dropdown>
74
+ </template>
75
+ </d2l-demo-snippet>
76
+
77
+ <h2>Dropdown (in a dialog)</h2>
78
+
79
+ <d2l-demo-snippet>
80
+ <template>
81
+ <d2l-button id="openDialog1">Show Dialog</d2l-button>
82
+ <d2l-dialog id="dialog1" title-text="Dialog Title">
83
+ <div>
84
+ <p>Bilge tack furl dance the hempen jig fathom weigh anchor mizzen Blimey Jack Ketch flogging. Lee galleon avast schooner long clothes scuppers pinnace bucko deadlights gibbet. Nipper brigantine Buccaneer Gold Road matey gangway booty tender killick Brethren of the Coast.</p>
85
+ <d2l-dropdown prefer-fixed-positioning>
86
+ <d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
87
+ <d2l-dropdown-content max-width="400" prefer-fixed-positioning>
88
+ <a href="https://youtu.be/9ze87zQFSak">Google</a>
89
+ <p>Shrouds hulk ye run a rig pink wherry hornswaggle overhaul spike splice the main brace. Barbary Coast salmagundi Nelsons folly lanyard Sea Legs topgallant Sink me crow's nest scuttle red ensign. Handsomely swab wench hang the jib square-rigged scuppers spyglass holystone Yellow Jack splice the main brace.</p>
90
+ <a href="http://www.desire2learn.com">D2L</a>
91
+ </d2l-dropdown-content>
92
+ </d2l-dropdown>
93
+ <p>Piracy bowsprit Arr shrouds salmagundi scuttle heave down doubloon trysail Jack Ketch. Killick boom Jolly Roger Pieces of Eight crack Jennys tea cup Cat o'nine tails league Privateer topgallant lanyard. Cat o'nine tails coxswain scurvy spirits keelhaul quarterdeck matey nipper scallywag Jolly Roger.</p>
94
+ <p>Clap of thunder aye Corsair Barbary Coast prow shrouds schooner keel topmast code of conduct. Matey case shot spirits Davy Jones' Locker draft schooner Brethren of the Coast barkadeer jury mast measured fer yer chains. Bilge rat run a rig gaff warp loot clipper belaying pin main sheet lanyard avast.</p>
95
+ <p>Pieces of Eight lookout Letter of Marque mutiny tender spanker Jack Ketch long clothes crow's nest line. Lass draught six pounders spirits skysail jib American Main chase hulk coxswain. Run a shot across the bow galleon Cat o'nine tails brigantine reef Admiral of the Black wherry quarterdeck keelhaul coffer.</p>
96
+ </div>
97
+ <d2l-button slot="footer" primary data-dialog-action="ok">Click Me!</d2l-button>
98
+ <d2l-button slot="footer" data-dialog-action>Cancel</d2l-button>
99
+ </d2l-dialog>
100
+ <script>
101
+ document.querySelector('#openDialog1').addEventListener('click', () => {
102
+ document.querySelector('#dialog1').opened = true;
103
+ });
104
+ </script>
105
+ </template>
106
+ </d2l-demo-snippet>
107
+
108
+ <h2>Dropdown (with DOM mutation)</h2>
109
+
110
+ <d2l-demo-snippet>
111
+ <template>
112
+ <div>
113
+ <div id="mutations-above"></div>
114
+ <d2l-dropdown prefer-fixed-positioning>
115
+ <d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
116
+ <d2l-dropdown-content max-width="400" prefer-fixed-positioning>
117
+ <d2l-button-subtle id="mutations-add-above">Add to Above</d2l-button-subtle>
118
+ </d2l-dropdown-content>
119
+ </d2l-dropdown>
120
+ </div>
121
+ <script>
122
+ document.querySelector('#mutations-add-above').addEventListener('click', e => {
123
+ const mutationsContainer = e.target.parentNode.parentNode.parentNode.querySelector('#mutations-above');
124
+ const newContent = document.createElement('p');
125
+ newContent.innerText = 'Blimey brigantine gangplank booty rope\'s end lugger heave down run a rig Yellow Jack dead men tell no tales. Pirate Round scuppers spanker hogshead Davy Jone\'s Locker heave down wench fluke marooned boom. Lanyard salmagundi careen doubloon swing the lead shrouds crow\'s nest parrel gun pressgang.';
126
+ mutationsContainer.appendChild(newContent);
127
+ });
128
+ </script>
129
+ </template>
130
+ </d2l-demo-snippet>
131
+
132
+ <script>
133
+ document.querySelector('d2l-demo-page').pageTitle = `d2l-dropdown (${ window.D2L.LP.Web.UI.Flags.Flag('GAUD-131-dropdown-fixed-positioning', false) ? 'fixed' : 'relative' })`;
134
+ </script>
135
+ </body>
136
+
137
+ </html>
@@ -2,7 +2,7 @@ import '../backdrop/backdrop.js';
2
2
  import '../button/button.js';
3
3
  import '../focus-trap/focus-trap.js';
4
4
  import { clearDismissible, setDismissible } from '../../helpers/dismissible.js';
5
- import { findComposedAncestor, getBoundingAncestor, isComposedAncestor, isVisible } from '../../helpers/dom.js';
5
+ import { findComposedAncestor, getBoundingAncestor, getComposedParent, isComposedAncestor, isVisible } from '../../helpers/dom.js';
6
6
  import { getComposedActiveElement, getFirstFocusableDescendant, getPreviousFocusableAncestor } from '../../helpers/focus.js';
7
7
  import { classMap } from 'lit/directives/class-map.js';
8
8
  import { html } from 'lit';
@@ -284,6 +284,7 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
284
284
  this._showBackdrop = false;
285
285
  this._verticalOffset = defaultVerticalOffset;
286
286
 
287
+ this.__reposition = this.__reposition.bind(this);
287
288
  this.__onResize = this.__onResize.bind(this);
288
289
  this.__onAutoCloseFocus = this.__onAutoCloseFocus.bind(this);
289
290
  this.__onAutoCloseClick = this.__onAutoCloseClick.bind(this);
@@ -316,6 +317,7 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
316
317
  this.mediaQueryList = window.matchMedia(`(max-width: ${this.mobileBreakpointOverride - 1}px)`);
317
318
  this._useMobileStyling = this.mediaQueryList.matches;
318
319
  if (this.mediaQueryList.addEventListener) this.mediaQueryList.addEventListener('change', this._handleMobileResize);
320
+ if (this.opened) this.__addRepositionHandlers();
319
321
  }
320
322
 
321
323
  disconnectedCallback() {
@@ -330,6 +332,7 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
330
332
  this.__dismissibleId = null;
331
333
 
332
334
  if (this.__resizeObserver) this.__resizeObserver.disconnect();
335
+ this.__removeRepositionHandlers();
333
336
  }
334
337
 
335
338
  firstUpdated(changedProperties) {
@@ -447,6 +450,33 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
447
450
  }
448
451
  }
449
452
 
453
+ __addRepositionHandlers() {
454
+ if (!this._fixedPositioning) return;
455
+
456
+ const isScrollable = (node, prop) => {
457
+ const value = window.getComputedStyle(node, null).getPropertyValue(prop);
458
+ return (value === 'scroll' || value === 'auto');
459
+ };
460
+
461
+ let node = this;
462
+ this.__removeRepositionHandlers();
463
+ this._scrollablesObserved = [];
464
+ while (node) {
465
+ let observeScrollable = false;
466
+ if (node.nodeType === Node.ELEMENT_NODE) {
467
+ observeScrollable = isScrollable(node, 'overflow-y') || isScrollable(node, 'overflow-x');
468
+ } else if (node.nodeType === Node.DOCUMENT_NODE) {
469
+ observeScrollable = true;
470
+ }
471
+ if (observeScrollable) {
472
+ this._scrollablesObserved.push(node);
473
+ node.addEventListener('scroll', this.__reposition);
474
+ }
475
+ node = getComposedParent(node);
476
+ }
477
+
478
+ }
479
+
450
480
  __disconnectResizeObserver(entries) {
451
481
  for (let i = 0; i < entries.length; i++) {
452
482
  const entry = entries[i];
@@ -617,8 +647,12 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
617
647
 
618
648
  await doOpen();
619
649
 
650
+ this.__addRepositionHandlers();
651
+
620
652
  } else {
621
653
 
654
+ this.__removeRepositionHandlers();
655
+
622
656
  if (this.__dismissibleId) {
623
657
  clearDismissible(this.__dismissibleId);
624
658
  this.__dismissibleId = null;
@@ -755,6 +789,27 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
755
789
  await adjustPosition();
756
790
  }
757
791
 
792
+ __removeRepositionHandlers() {
793
+ if (!this._fixedPositioning) return;
794
+ if (!this._scrollablesObserved) return;
795
+
796
+ this._scrollablesObserved.forEach(node => {
797
+ node.removeEventListener('scroll', this.__reposition);
798
+ });
799
+ this._scrollablesObserved = null;
800
+ }
801
+
802
+ __reposition() {
803
+ // throttle repositioning (https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event#scroll_event_throttling)
804
+ if (!this.__repositioning) {
805
+ requestAnimationFrame(() => {
806
+ this.__position(true);
807
+ this.__repositioning = false;
808
+ });
809
+ }
810
+ this.__repositioning = true;
811
+ }
812
+
758
813
  __toggleScrollStyles() {
759
814
  /* scrollHeight incorrect in IE by 4px second time opened */
760
815
  this._bottomOverflow = this.__content.scrollHeight - (this.__content.scrollTop + this.__content.clientHeight) >= 5;
@@ -204,7 +204,7 @@ The `d2l-filter-dimension-set` component is the main dimension type that will wo
204
204
 
205
205
  ## Dimension Set Value [d2l-filter-dimension-set-value]
206
206
 
207
- This component is built to be used alongside the [d2l-filter-dimension-set](#d2l-filter-dimension-set) component, this will give you a selectable list of filter values.
207
+ This component is built to be used alongside the [d2l-filter-dimension-set](#d2l-filter-dimension-set) component. It will give you a selectable list of filter values.
208
208
 
209
209
  <!-- docs: demo code properties name:d2l-filter-dimension-set-value align:start autoOpen:true autoSize:false size:large -->
210
210
  ```html
@@ -233,11 +233,41 @@ This component is built to be used alongside the [d2l-filter-dimension-set](#d2l
233
233
  | `selected` | Boolean, default: `false` | Whether the value in the filter is selected or not |
234
234
  <!-- docs: end hidden content -->
235
235
 
236
+ ## Dimension Set Value: Preset Date Range [d2l-filter-dimension-set-date-text-value]
237
+
238
+ This component is built to be used alongside the [d2l-filter-dimension-set](#d2l-filter-dimension-set) component. It will give you a selectable filter value based on the `range` defined on the component, which is to be one of a set of pre-defined range options. Selection triggers the `d2l-filter-change` event, with `start-value` and `end-value` (in UTC) being included in the changes for the `selected` item.
239
+
240
+ <!-- docs: demo code properties name:d2l-filter-dimension-set-date-text-value align:start autoOpen:true autoSize:false size:large -->
241
+ ```html
242
+ <script type="module">
243
+ import '@brightspace-ui/core/components/filter/filter.js';
244
+ import '@brightspace-ui/core/components/filter/filter-dimension-set.js';
245
+ import '@brightspace-ui/core/components/filter/filter-dimension-set-date-text-value.js';
246
+ </script>
247
+ <d2l-filter>
248
+ <d2l-filter-dimension-set key="dates" text="Dates">
249
+ <d2l-filter-dimension-set-date-text-value key="lastHour" range="lastHour" selected></d2l-filter-dimension-set-date-text-value>
250
+ <d2l-filter-dimension-set-date-text-value key="48hours" range="48hours" disabled></d2l-filter-dimension-set-date-text-value>
251
+ <d2l-filter-dimension-set-date-text-value key="14days" range="14days"></d2l-filter-dimension-set-date-text-value>
252
+ </d2l-filter-dimension-set>
253
+ </d2l-filter>
254
+ ```
255
+ <!-- docs: start hidden content -->
256
+ ### Properties
257
+
258
+ | Property | Type | Description |
259
+ |---|---|---|
260
+ | `key` | String, required | Unique identifier within a dimension for the value |
261
+ | `range` | String, required | The preset date/time range that the list item represents. Value is to be one of 'today', 'lastHour', '24hours', '48hours', '7days', '14days', '30days', or '6months'. |
262
+ | `disabled` | Boolean, default: `false` | Whether the value in the filter is disabled or not |
263
+ | `selected` | Boolean, default: `false` | Whether the value in the filter is selected or not |
264
+ <!-- docs: end hidden content -->
265
+
236
266
  ## Search and Paging
237
267
 
238
268
  Most filters will not need search or paging features since filter value lists are generally short. For longer lists of filter values when Search is necessary, it can be enabled by setting search-type to `automatic` or `manual`.
239
269
 
240
- `automatic` search runs a basic case-insensitive text comparison on the dimension values that are loaded in the browser, having no awareness of server-side values that are not yet loaded.
270
+ `automatic` search runs a basic case-insensitive text comparison on the dimension values that are loaded in the browser, having no awareness of server-side values that are not yet loaded.
241
271
 
242
272
  `manual` search dispatches a `d2l-filter-dimension-search` event delegating the search to the component's consumer. The event's detail will contain the key of the dimension from where the event was dispatched (`key`), the text value used for the search (`value`) and a callback (`searchCompleteCallback`). This callback gives the consumer control of which keys to display, either by setting `displayAllKeys` to `true` or passing a list of the keys to display as `keysToDisplay` (all other keys will be hidden). The dimension will be in a loading state until the callback is called.
243
273
  ```js
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
2
+ <html lang="en" data-timezone='{"name":"Canada - Toronto", "identifier":"America/Toronto"}'>
3
3
 
4
4
  <head>
5
5
  <link rel="stylesheet" href="../../demo/styles.css" type="text/css">
@@ -10,6 +10,7 @@
10
10
  import '../../filter/filter-dimension-set.js';
11
11
  import '../../filter/filter-dimension-set-empty-state.js';
12
12
  import '../../filter/filter-dimension-set-value.js';
13
+ import '../../filter/filter-dimension-set-date-text-value.js';
13
14
  import './filter-search-demo.js';
14
15
  import './filter-load-more-demo.js';
15
16
  </script>
@@ -127,7 +128,7 @@
127
128
  <d2l-filter-dimension-set-value key="stats" text="Statistics"></d2l-filter-dimension-set-value>
128
129
  <d2l-filter-dimension-set-value key="writerscraft" text="Writer's Craft"></d2l-filter-dimension-set-value>
129
130
  </d2l-filter-dimension-set>
130
-
131
+
131
132
  </d2l-filter>
132
133
  <d2l-filter text="More Filters">
133
134
  <d2l-filter-dimension-set key="course" text="Course" loading select-all></d2l-filter-dimension-set>
@@ -155,6 +156,21 @@
155
156
  <d2l-filter-search-demo></d2l-filter-search-demo>
156
157
  </template>
157
158
  </d2l-demo-snippet>
159
+
160
+ <h2>Date Filter</h2>
161
+ <d2l-demo-snippet>
162
+ <template>
163
+ <d2l-filter id="filter-single">
164
+ <d2l-filter-dimension-set key="dates" text="Dates">
165
+ <d2l-filter-dimension-set-value key="lastweek" text="Last Week"></d2l-filter-dimension-set-value>
166
+ <d2l-filter-dimension-set-date-text-value key="lastHour" range="lastHour" selected></d2l-filter-dimension-set-date-text-value>
167
+ <d2l-filter-dimension-set-date-text-value key="48hours" range="48hours" disabled></d2l-filter-dimension-set-date-text-value>
168
+ <d2l-filter-dimension-set-date-text-value key="14days" range="14days"></d2l-filter-dimension-set-date-text-value>
169
+ <d2l-filter-dimension-set-date-text-value key="6months" range="6months"></d2l-filter-dimension-set-date-text-value>
170
+ </d2l-filter-dimension-set>
171
+ </d2l-filter>
172
+ </template>
173
+ </d2l-demo-snippet>
158
174
  </d2l-demo-page>
159
175
 
160
176
  <script type="module">
@@ -0,0 +1,156 @@
1
+ import { getUTCDateTimeRange } from '../../helpers/dateTime.js';
2
+ import { LitElement } from 'lit';
3
+ import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
4
+
5
+ /**
6
+ * A component to represent a possible date value that can be selected for a dimension set (the main filter dimension type) for predefined date ranges.
7
+ * A range property is used to define the preset text shown, as well as the start and end date values formatted as UTC strings.
8
+ * This component does not render anything, but instead gathers data needed for the d2l-filter.
9
+ */
10
+ class FilterDimensionSetDateTextValue extends LocalizeCoreElement(LitElement) {
11
+
12
+ static get properties() {
13
+ return {
14
+ /**
15
+ * Whether this value in the filter is disabled or not
16
+ * @type {boolean}
17
+ */
18
+ disabled: { type: Boolean, reflect: true },
19
+ /**
20
+ * @ignore
21
+ */
22
+ endValue: { type: String },
23
+ /**
24
+ * REQUIRED: Unique key to represent this value in the dimension
25
+ * @type {string}
26
+ */
27
+ key: { type: String },
28
+ /**
29
+ * REQUIRED: The preset date/time range that the list item represents
30
+ * @type {'today'|'lastHour'|'24hours'|'48hours'|'7days'|'14days'|'30days'|'6months'}
31
+ */
32
+ range: { type: String },
33
+ /**
34
+ * Whether this value in the filter is selected or not
35
+ * @type {boolean}
36
+ */
37
+ selected: { type: Boolean, reflect: true },
38
+ /**
39
+ * @ignore
40
+ */
41
+ startValue: { type: String },
42
+ /**
43
+ * @ignore
44
+ */
45
+ text: { type: String, reflect: true }
46
+ };
47
+ }
48
+
49
+ constructor() {
50
+ super();
51
+ this.disabled = false;
52
+ this.selected = false;
53
+ this.text = '';
54
+ this._enforceSingleSelection = true;
55
+ this._filterSetValue = true;
56
+ this._noSearchSupport = true;
57
+ }
58
+
59
+ firstUpdated(changedProperties) {
60
+ super.firstUpdated(changedProperties);
61
+
62
+ if (this.selected && this.range) {
63
+ // if the value is initially selected, startValue and endValue should be set in case used by consumer
64
+ const dateTimeRange = getUTCDateTimeRange(this._rangeDetails?.rangeType, this._rangeDetails?.rangeNum);
65
+ this.startValue = dateTimeRange?.startValue;
66
+ this.endValue = dateTimeRange?.endValue;
67
+ }
68
+ }
69
+
70
+ updated(changedProperties) {
71
+ super.updated(changedProperties);
72
+
73
+ const changes = new Map();
74
+ changedProperties.forEach((oldValue, prop) => {
75
+ if (oldValue === undefined && prop !== 'text') return;
76
+
77
+ if (prop === 'disabled' || prop === 'selected' || prop === 'text') {
78
+ changes.set(prop, this[prop]);
79
+ } else if (prop === 'range') {
80
+ changes.set('rangeType', this._rangeDetails?.rangeType);
81
+ changes.set('rangeNum', this._rangeDetails?.rangeNum);
82
+ }
83
+ });
84
+ if (changes.size > 0) {
85
+ /** @ignore */
86
+ this.dispatchEvent(new CustomEvent('d2l-filter-dimension-set-value-data-change', {
87
+ detail: { valueKey: this.key, changes: changes },
88
+ bubbles: true,
89
+ composed: false
90
+ }));
91
+ }
92
+ }
93
+
94
+ willUpdate(changedProperties) {
95
+ super.willUpdate(changedProperties);
96
+
97
+ if (changedProperties.has('range')) this._handleRangeUpdated();
98
+ }
99
+
100
+ getValueDetails() {
101
+ this._handleRangeUpdated();
102
+
103
+ return {
104
+ disabled: this.disabled,
105
+ key: this.key,
106
+ selected: this.selected,
107
+ text: this.text,
108
+ getAdditionalEventDetails: this._getAdditionalEventDetails.bind(this)
109
+ };
110
+ }
111
+
112
+ _getAdditionalEventDetails(selected) {
113
+ if (!selected) return {};
114
+ const dateTimeRange = getUTCDateTimeRange(this._rangeDetails?.rangeType, this._rangeDetails?.rangeNum);
115
+ return { startValue: dateTimeRange.startValue, endValue: dateTimeRange.endValue };
116
+ }
117
+
118
+ _handleRangeUpdated() {
119
+ switch (this.range) {
120
+ case 'today':
121
+ this._rangeDetails = { rangeType: 'days', rangeNum: 0 };
122
+ break;
123
+ case 'lastHour':
124
+ this._rangeDetails = { rangeType: 'hours', rangeNum: -1 };
125
+ break;
126
+ case '24hours':
127
+ this._rangeDetails = { rangeType: 'hours', rangeNum: -24 };
128
+ break;
129
+ case '48hours':
130
+ this._rangeDetails = { rangeType: 'hours', rangeNum: -48 };
131
+ break;
132
+ case '7days':
133
+ this._rangeDetails = { rangeType: 'days', rangeNum: -7 };
134
+ break;
135
+ case '14days':
136
+ this._rangeDetails = { rangeType: 'days', rangeNum: -14 };
137
+ break;
138
+ case '30days':
139
+ this._rangeDetails = { rangeType: 'days', rangeNum: -30 };
140
+ break;
141
+ case '6months':
142
+ this._rangeDetails = { rangeType: 'months', rangeNum: -6 };
143
+ break;
144
+ default:
145
+ console.warn('d2l-filter-dimension-set-date-text-value: Invalid range value');
146
+ this._rangeDetails = {};
147
+ break;
148
+ }
149
+
150
+ if (this._rangeDetails.rangeType === 'hours') this.text = this.localize('components.filter-dimension-set-date-text-value.textHours', { num: Math.abs(this._rangeDetails.rangeNum) });
151
+ else if (this._rangeDetails.rangeType === 'days') this.text = this.localize('components.filter-dimension-set-date-text-value.textDays', { num: Math.abs(this._rangeDetails.rangeNum) });
152
+ else if (this._rangeDetails.rangeType === 'months') this.text = this.localize('components.filter-dimension-set-date-text-value.textMonths', { num: Math.abs(this._rangeDetails.rangeNum) });
153
+ }
154
+ }
155
+
156
+ customElements.define('d2l-filter-dimension-set-date-text-value', FilterDimensionSetDateTextValue);
@@ -41,6 +41,9 @@ class FilterDimensionSetValue extends LitElement {
41
41
  this.disabled = false;
42
42
  this.selected = false;
43
43
  this.text = '';
44
+ this._enforceSingleSelection = false;
45
+ this._filterSetValue = true;
46
+ this._noSearchSupport = false;
44
47
  }
45
48
 
46
49
  get count() {
@@ -78,6 +81,16 @@ class FilterDimensionSetValue extends LitElement {
78
81
  }));
79
82
  }
80
83
  }
84
+
85
+ getValueDetails() {
86
+ return {
87
+ count: this.count,
88
+ disabled: this.disabled,
89
+ key: this.key,
90
+ selected: this.selected,
91
+ text: this.text
92
+ };
93
+ }
81
94
  }
82
95
 
83
96
  customElements.define('d2l-filter-dimension-set-value', FilterDimensionSetValue);
@@ -136,15 +136,16 @@ class FilterDimensionSet extends LitElement {
136
136
 
137
137
  getValues() {
138
138
  const valueNodes = this._getSlottedNodes();
139
+ let noSearchSupport = false;
140
+ let enforceSingleSelection = false;
139
141
  const values = valueNodes.map(value => {
140
- return {
141
- count: value.count,
142
- disabled: value.disabled,
143
- key: value.key,
144
- selected: value.selected,
145
- text: value.text
146
- };
142
+ if (value._noSearchSupport) noSearchSupport = true;
143
+ if (value._enforceSingleSelection) enforceSingleSelection = true;
144
+
145
+ return value.getValueDetails();
147
146
  });
147
+ if (noSearchSupport) this.searchType = 'none';
148
+ if (enforceSingleSelection) this.selectionSingle = true;
148
149
  return values;
149
150
  }
150
151
 
@@ -173,7 +174,10 @@ class FilterDimensionSet extends LitElement {
173
174
  _getSlottedNodes() {
174
175
  if (!this._slot) return [];
175
176
  const nodes = this._slot.assignedNodes({ flatten: true });
176
- return nodes.filter((node) => node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'd2l-filter-dimension-set-value');
177
+ return nodes.filter((node) => {
178
+ if (node.nodeType !== Node.ELEMENT_NODE) return false;
179
+ return node._filterSetValue;
180
+ });
177
181
  }
178
182
 
179
183
  _handleDimensionSetValueDataChange(e) {
@@ -686,7 +686,7 @@ class Filter extends FocusMixin(LocalizeCoreElement(RtlMixin(LitElement))) {
686
686
  _handleDimensionDataChange(e) {
687
687
  const changes = e.detail.changes;
688
688
  const dimension = this._getDimensionByKey(e.detail.dimensionKey);
689
- const value = e.detail.valueKey && dimension.values.find(value => value.key === e.detail.valueKey);
689
+ const value = e.detail.valueKey && dimension?.values.find(value => value.key === e.detail.valueKey);
690
690
  const toUpdate = e.detail.valueKey ? value : dimension;
691
691
 
692
692
  if (!toUpdate) return;
@@ -869,7 +869,11 @@ class Filter extends FocusMixin(LocalizeCoreElement(RtlMixin(LitElement))) {
869
869
  this._totalAppliedCount--;
870
870
  }
871
871
 
872
- this._dispatchChangeEvent(dimension, { valueKey: valueKey, selected: selected });
872
+ const details = { valueKey: valueKey, selected: selected };
873
+
874
+ if (value.getAdditionalEventDetails) Object.assign(details, value.getAdditionalEventDetails(selected));
875
+
876
+ this._dispatchChangeEvent(dimension, details);
873
877
  }
874
878
 
875
879
  _performDimensionClear(dimension) {
@@ -3802,6 +3802,72 @@
3802
3802
  "name": "d2l-filter-search-demo",
3803
3803
  "path": "./components/filter/demo/filter-search-demo.js"
3804
3804
  },
3805
+ {
3806
+ "name": "d2l-filter-dimension-set-date-text-value",
3807
+ "path": "./components/filter/filter-dimension-set-date-text-value.js",
3808
+ "description": "A component to represent a possible date value that can be selected for a dimension set (the main filter dimension type) for predefined date ranges.\nA range property is used to define the preset text shown, as well as the start and end date values formatted as UTC strings.\nThis component does not render anything, but instead gathers data needed for the d2l-filter.",
3809
+ "attributes": [
3810
+ {
3811
+ "name": "key",
3812
+ "description": "REQUIRED: Unique key to represent this value in the dimension",
3813
+ "type": "string"
3814
+ },
3815
+ {
3816
+ "name": "range",
3817
+ "description": "REQUIRED: The preset date/time range that the list item represents",
3818
+ "type": "'today'|'lastHour'|'24hours'|'48hours'|'7days'|'14days'|'30days'|'6months'"
3819
+ },
3820
+ {
3821
+ "name": "disabled",
3822
+ "description": "Whether this value in the filter is disabled or not",
3823
+ "type": "boolean",
3824
+ "default": "false"
3825
+ },
3826
+ {
3827
+ "name": "selected",
3828
+ "description": "Whether this value in the filter is selected or not",
3829
+ "type": "boolean",
3830
+ "default": "false"
3831
+ }
3832
+ ],
3833
+ "properties": [
3834
+ {
3835
+ "name": "key",
3836
+ "attribute": "key",
3837
+ "description": "REQUIRED: Unique key to represent this value in the dimension",
3838
+ "type": "string"
3839
+ },
3840
+ {
3841
+ "name": "range",
3842
+ "attribute": "range",
3843
+ "description": "REQUIRED: The preset date/time range that the list item represents",
3844
+ "type": "'today'|'lastHour'|'24hours'|'48hours'|'7days'|'14days'|'30days'|'6months'"
3845
+ },
3846
+ {
3847
+ "name": "disabled",
3848
+ "attribute": "disabled",
3849
+ "description": "Whether this value in the filter is disabled or not",
3850
+ "type": "boolean",
3851
+ "default": "false"
3852
+ },
3853
+ {
3854
+ "name": "selected",
3855
+ "attribute": "selected",
3856
+ "description": "Whether this value in the filter is selected or not",
3857
+ "type": "boolean",
3858
+ "default": "false"
3859
+ },
3860
+ {
3861
+ "name": "text",
3862
+ "type": "string",
3863
+ "default": "\"\""
3864
+ },
3865
+ {
3866
+ "name": "documentLocaleSettings",
3867
+ "default": "\"getDocumentLocaleSettings()\""
3868
+ }
3869
+ ]
3870
+ },
3805
3871
  {
3806
3872
  "name": "d2l-filter-dimension-set-empty-state",
3807
3873
  "path": "./components/filter/filter-dimension-set-empty-state.js",
@@ -161,6 +161,53 @@ export function getUTCDateTimeFromLocalDateTime(date, time) {
161
161
  return formatDateTimeInISO(utcDateTime);
162
162
  }
163
163
 
164
+ export function getUTCDateTimeRange(type, diff) {
165
+ if (!type || (!diff && diff !== 0) || (diff === 0 && type !== 'days')) return {};
166
+ if (type !== 'seconds' && type !== 'minutes' && type !== 'hours' && type !== 'days' && type !== 'months' && type !== 'years') return {};
167
+
168
+ if (type === 'days' && diff === 0) {
169
+ // assume "today" (midnight in user's time to 23:59:59 in user's time)
170
+ const today = formatDateInISO(getToday());
171
+ const startValue = getUTCDateTimeFromLocalDateTime(today, '0:0:0');
172
+ const endValue = getUTCDateTimeFromLocalDateTime(today, '23:59:59');
173
+ return { startValue, endValue };
174
+ }
175
+
176
+ /**
177
+ * If diff is positive, range is in the future. Start date is now and end date is in the future.
178
+ * If diff is negative, range is in the past. End date is now and start date is in the past.
179
+ */
180
+
181
+ const rangeDate = new Date();
182
+ const nowUTCString = rangeDate.toISOString();
183
+
184
+ if (type === 'seconds') {
185
+ const newSeconds = rangeDate.getUTCSeconds() + diff;
186
+ rangeDate.setUTCSeconds(newSeconds);
187
+ } else if (type === 'minutes') {
188
+ const newMinutes = rangeDate.getUTCMinutes() + diff;
189
+ rangeDate.setUTCMinutes(newMinutes);
190
+ } else if (type === 'hours') {
191
+ const newHours = rangeDate.getUTCHours() + diff;
192
+ rangeDate.setUTCHours(newHours);
193
+ } else if (type === 'days') {
194
+ const newDate = rangeDate.getUTCDate() + diff;
195
+ rangeDate.setUTCDate(newDate);
196
+ } else if (type === 'months') {
197
+ const newMonth = rangeDate.getUTCMonth() + diff;
198
+ rangeDate.setUTCMonth(newMonth);
199
+ } else if (type === 'years') {
200
+ const newYear = rangeDate.getUTCFullYear() + diff;
201
+ rangeDate.setUTCFullYear(newYear);
202
+ }
203
+
204
+ if (diff > 0) {
205
+ return { startValue: nowUTCString, endValue: rangeDate.toISOString() };
206
+ } else {
207
+ return { startValue: rangeDate.toISOString(), endValue: nowUTCString };
208
+ }
209
+ }
210
+
164
211
  export function isDateInRange(date, min, max) {
165
212
  if (!date) return false;
166
213
  const afterMin = !min || (min && date.getTime() >= min.getTime());
package/lang/ar.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {ما من نتائج بحث} one {{number} نتيجة بحث}‏ other {{number} من نتائج البحث}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. تظهر عوامل التصفية المحددة أولاً.",
27
27
  "components.filter.singleDimensionDescription": "التصفية حسب: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} غير صالحة.",
29
32
  "components.form-element.defaultFieldLabel": "الحقل",
30
33
  "components.form-element.input.email.typeMismatch": "البريد الإلكتروني غير صالح",
package/lang/cy.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {Dim canlyniadau chwilio} one {{number} canlyniad chwilio} other {{number} canlyniadau chwilio}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Mae'r hidlyddion a ddewiswyd yn ymddangos gyntaf.",
27
27
  "components.filter.singleDimensionDescription": "Hidlo yn ôl: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "Mae {label} yn annilys.",
29
32
  "components.form-element.defaultFieldLabel": "Maes",
30
33
  "components.form-element.input.email.typeMismatch": "Nid yw'r e-bost yn ddilys",
package/lang/da.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {Ingen søgeresultater} one {{number} søgeresultat} other {{number} søgeresultater}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Valgte filtre vises først.",
27
27
  "components.filter.singleDimensionDescription": "Filtrer efter: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} er ugyldigt.",
29
32
  "components.form-element.defaultFieldLabel": "Felt",
30
33
  "components.form-element.input.email.typeMismatch": "E-mail er ikke gyldig",
package/lang/de.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {Kein Suchergebnis} one {{number} Suchergebnis} other {{number} Suchergebnisse}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Ausgewählte Filter werden zuerst angezeigt.",
27
27
  "components.filter.singleDimensionDescription": "Filtern nach: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} ist ungültig.",
29
32
  "components.form-element.defaultFieldLabel": "Feld",
30
33
  "components.form-element.input.email.typeMismatch": "Die E-Mail-Adresse ist ungültig",
package/lang/en-gb.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {No search results} one {{number} search result} other {{number} search results}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Selected filters appear first.",
27
27
  "components.filter.singleDimensionDescription": "Filter by: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} is invalid.",
29
32
  "components.form-element.defaultFieldLabel": "Field",
30
33
  "components.form-element.input.email.typeMismatch": "Email is not valid",
package/lang/en.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {No search results} one {{number} search result} other {{number} search results}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Selected filters appear first.",
27
27
  "components.filter.singleDimensionDescription": "Filter by: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} is invalid.",
29
32
  "components.form-element.defaultFieldLabel": "Field",
30
33
  "components.form-element.input.email.typeMismatch": "Email is not valid",
package/lang/es-es.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {No hay resultados de búsqueda} one {{number} resultado de búsqueda} other {{number} resultados de búsqueda}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Los filtros seleccionados aparecen primero.",
27
27
  "components.filter.singleDimensionDescription": "Filtrar por: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} no es válido.",
29
32
  "components.form-element.defaultFieldLabel": "Campo",
30
33
  "components.form-element.input.email.typeMismatch": "El correo electrónico no es válido",
package/lang/es.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {No se encontraron resultados de búsqueda} one {{number} resultado de búsqueda} other {{number} resultados de búsqueda}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Los filtros seleccionados aparecen primero.",
27
27
  "components.filter.singleDimensionDescription": "Filtrar por: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} no es válida.",
29
32
  "components.form-element.defaultFieldLabel": "Campo",
30
33
  "components.form-element.input.email.typeMismatch": "El correo electrónico no es válido",
package/lang/fr-fr.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {Aucun résultat de recherche} one {{number} résultat de recherche} other {{number} résultats de recherche}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Les filtres sélectionnés s’affichent en premier.",
27
27
  "components.filter.singleDimensionDescription": "Filtrer par : {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} n'est pas valide.",
29
32
  "components.form-element.defaultFieldLabel": "Champ",
30
33
  "components.form-element.input.email.typeMismatch": "L'adresse e-mail n'est pas valide.",
package/lang/fr.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {Aucun résultat de recherche} one {{number} résultat de recherche} other {{number} résultats de recherche}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Les filtres sélectionnés s’affichent en premier.",
27
27
  "components.filter.singleDimensionDescription": "Filtrer par : {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} n'est pas valide.",
29
32
  "components.form-element.defaultFieldLabel": "Champ",
30
33
  "components.form-element.input.email.typeMismatch": "L'adresse courriel n'est pas valide",
package/lang/hi.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {कोई खोज परिणाम नहीं} one {{number} खोज परिणाम} other {{number} खोज परिणाम}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. चुने गए फ़िल्टर सबसे पहले दिखाई देते हैं।",
27
27
  "components.filter.singleDimensionDescription": "इसके अनुसार फ़िल्टर करें: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} अमान्य है।",
29
32
  "components.form-element.defaultFieldLabel": "फ़ील्ड",
30
33
  "components.form-element.input.email.typeMismatch": "ईमेल मान्य नहीं है",
package/lang/ja.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {検索結果なし} other {{number} 件の検索結果}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}。選択したフィルタが最初に表示されます。",
27
27
  "components.filter.singleDimensionDescription": "フィルタ条件: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} は無効です。",
29
32
  "components.form-element.defaultFieldLabel": "フィールド",
30
33
  "components.form-element.input.email.typeMismatch": "電子メールが無効です",
package/lang/ko.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {검색 결과 없음} other {{number}개 검색 결과}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. 선택한 필터가 먼저 나타납니다.",
27
27
  "components.filter.singleDimensionDescription": "필터 기준: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label}이(가) 잘못되었습니다.",
29
32
  "components.form-element.defaultFieldLabel": "필드",
30
33
  "components.form-element.input.email.typeMismatch": "이메일이 유효하지 않습니다.",
package/lang/nl.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {Geen zoekresultaten} one {{number} zoekresultaat} other {{number} zoekresultaten}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Geselecteerde filters verschijnen als eerste.",
27
27
  "components.filter.singleDimensionDescription": "Filter op {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} is ongeldig.",
29
32
  "components.form-element.defaultFieldLabel": "Veld",
30
33
  "components.form-element.input.email.typeMismatch": "E-mailadres is ongeldig",
package/lang/pt.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {Sem resultados para a pesquisa} one {{number} resultado para a pesquisa} other {{number} resultados para a pesquisa}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Os filtros selecionados aparecem primeiro.",
27
27
  "components.filter.singleDimensionDescription": "Filtrar por: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} é inválido.",
29
32
  "components.form-element.defaultFieldLabel": "Campo",
30
33
  "components.form-element.input.email.typeMismatch": "E-mail inválido",
package/lang/sv.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {Inga sökresultat} one {{number} sökresultat} other {{number} sökresultat}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Valda filter visas först.",
27
27
  "components.filter.singleDimensionDescription": "Filtrera efter: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} är ogiltig.",
29
32
  "components.form-element.defaultFieldLabel": "Fält",
30
33
  "components.form-element.input.email.typeMismatch": "E-postadressen är ogiltig",
package/lang/tr.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {Arama sonucu yok} one {{number} arama sonucu} other {{number} arama sonucu}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}. Seçilen filtreler önce görünür.",
27
27
  "components.filter.singleDimensionDescription": "Filtreleme ölçütü: {filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, one {Last hour} other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} one {Last {num} days} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} geçersiz.",
29
32
  "components.form-element.defaultFieldLabel": "Alan",
30
33
  "components.form-element.input.email.typeMismatch": "E-posta geçerli değil",
package/lang/zh-cn.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {无搜索结果} other {{number} 个搜索结果}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}。先显示所选筛选器。",
27
27
  "components.filter.singleDimensionDescription": "筛选依据:{filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} 无效。",
29
32
  "components.form-element.defaultFieldLabel": "字段",
30
33
  "components.form-element.input.email.typeMismatch": "电子邮件无效",
package/lang/zh-tw.js CHANGED
@@ -25,6 +25,9 @@ export default {
25
25
  "components.filter.searchResults": "{number, plural, =0 {無搜尋結果} other {{number} 個搜尋結果}}",
26
26
  "components.filter.selectedFirstListLabel": "{headerText}。所選篩選器會先顯示。",
27
27
  "components.filter.singleDimensionDescription": "按此條件篩選:{filterName}",
28
+ "components.filter-dimension-set-date-text-value.textHours": "{num, plural, other {Last {num} hours}}",
29
+ "components.filter-dimension-set-date-text-value.textDays": "{num, plural, =0 {Today} other {Last {num} days}}",
30
+ "components.filter-dimension-set-date-text-value.textMonths": "Last {num} months",
28
31
  "components.form-element.defaultError": "{label} 無效。",
29
32
  "components.form-element.defaultFieldLabel": "欄位",
30
33
  "components.form-element.input.email.typeMismatch": "電子郵件無效",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.8.0",
3
+ "version": "3.9.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",
@@ -1,171 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
5
- <meta charset="UTF-8">
6
- <link rel="stylesheet" href="../../demo/styles.css" type="text/css">
7
- <script>
8
- window.D2L = { LP: { Web: { UI: { Flags: { Flag: () => true } } } } };
9
- </script>
10
- <script type="module">
11
- import '../../demo/demo-page.js';
12
- import '../../button/button.js';
13
- import '../../button/button-subtle.js';
14
- import '../../dialog/dialog.js';
15
- import '../dropdown.js';
16
- import '../dropdown-content.js';
17
- import { css, html, LitElement } from 'lit';
18
-
19
- class MutationDemo extends LitElement {
20
-
21
- static get properties() {
22
- return {
23
- count: { type: Number }
24
- };
25
- }
26
-
27
- static get styles() {
28
- return css`
29
- :host {
30
- border: 1px solid var(--d2l-color-tungsten);
31
- border-radius: 6px;
32
- display: block;
33
- margin: 1rem;
34
- position: relative;
35
- }
36
- div::before {
37
- border-bottom: 1px solid black;
38
- border-left: 1px solid black;
39
- content: 'WC';
40
- font-size: 0.7rem;
41
- padding: 0 0.2rem;
42
- position: absolute;
43
- right: 0;
44
- top: 0;
45
- }
46
- p {
47
- margin: 0.5rem;
48
- }
49
- `;
50
- }
51
-
52
- constructor() {
53
- super();
54
- this.count = 1;
55
- }
56
-
57
- render() {
58
- const elems = [];
59
- for (let i = 0; i < this.count; i++) {
60
- const newContent = document.createElement('p');
61
- newContent.innerText = 'Trysail Sail ho Corsair red ensign hulk smartly boom jib rum gangway.';
62
- elems.push(newContent);
63
- }
64
- return html`<div>${elems}</div>`;
65
- }
66
-
67
- }
68
- customElements.define('d2l-demo-mutation', MutationDemo);
69
-
70
- </script>
71
- </head>
72
-
73
- <body unresolved>
74
-
75
- <d2l-demo-page page-title="d2l-dropdown (fixed position)">
76
-
77
- <h2>Dropdown (mutation testing)</h2>
78
-
79
- <d2l-demo-snippet>
80
- <template>
81
- <div style="border: 1px solid var(--d2l-color-tungsten); border-radius: 6px; height: 600px; overflow: scroll;">
82
- <d2l-demo-mutation count="1"></d2l-demo-mutation>
83
- <div style="padding: 25px; position: relative;">
84
- <d2l-dropdown prefer-fixed-positioning>
85
- <d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
86
- <d2l-dropdown-content max-width="400" prefer-fixed-positioning>
87
- <d2l-button-subtle id="add-content1">Add to Light</d2l-button-subtle>
88
- <d2l-button-subtle id="add-content2">Add to Shadow</d2l-button-subtle>
89
- <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
90
- magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
91
- commodo consequat. </p>
92
- <a href="http://www.desire2learn.com">D2L</a>
93
- </d2l-dropdown-content>
94
- </d2l-dropdown>
95
- <br><br><br><br><br><br>
96
- </div>
97
- </div>
98
- <script>
99
- document.querySelector('#add-content1').addEventListener('click', e => {
100
- const dropdown = e.target.parentNode.parentNode;
101
- const newContent = document.createElement('p');
102
- newContent.innerText = 'Trysail Sail ho Corsair red ensign hulk smartly boom jib rum gangway. Case shot Shiver me timbers gangplank crack Jennys tea cup ballast Blimey lee snow crow\'s nest rutters. Fluke jib scourge of the seven seas boatswain schooner gaff booty Jack Tar transom spirits.';
103
- dropdown.parentNode.insertBefore(newContent, dropdown);
104
- });
105
- document.querySelector('#add-content2').addEventListener('click', () => {
106
- const mutationDemo = document.querySelector('d2l-demo-mutation');
107
- mutationDemo.count += 1;
108
- });
109
- </script>
110
- </template>
111
- </d2l-demo-snippet>
112
-
113
- <h2>Dropdown (transform container)</h2>
114
-
115
- <d2l-demo-snippet>
116
- <template>
117
-
118
- <div style="padding: 25px; transform: translate(0);">
119
- <d2l-dropdown prefer-fixed-positioning>
120
- <d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
121
- <d2l-dropdown-content max-width="400" prefer-fixed-positioning>
122
- <a href="https://youtu.be/9ze87zQFSak">Google</a>
123
- <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
124
- magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
125
- commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
126
- nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
127
- anim id est laborum.</p>
128
- <a href="http://www.desire2learn.com">D2L</a>
129
- </d2l-dropdown-content>
130
- </d2l-dropdown>
131
- </div>
132
-
133
- </template>
134
- </d2l-demo-snippet>
135
-
136
- <h2>Dropdown (in a dialog)</h2>
137
-
138
- <d2l-demo-snippet>
139
- <template>
140
- <d2l-button id="openDialog1">Show Dialog</d2l-button>
141
- <d2l-dialog id="dialog1" title-text="Dialog Title">
142
- <div>
143
- <p>Deadlights jack lad schooner scallywag dance the hempen jig carouser broadside cable strike colors. Bring a spring upon her cable holystone blow the man down spanker</p>
144
- <d2l-dropdown prefer-fixed-positioning>
145
- <d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
146
- <d2l-dropdown-content max-width="400" prefer-fixed-positioning>
147
- <a href="https://youtu.be/9ze87zQFSak">Google</a>
148
- <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
149
- magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
150
- commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
151
- nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
152
- anim id est laborum.</p>
153
- <a href="http://www.desire2learn.com">D2L</a>
154
- </d2l-dropdown-content>
155
- </d2l-dropdown>
156
- </div>
157
- <d2l-button slot="footer" primary data-dialog-action="ok">Click Me!</d2l-button>
158
- <d2l-button slot="footer" data-dialog-action>Cancel</d2l-button>
159
- </d2l-dialog>
160
- <script>
161
- document.querySelector('#openDialog1').addEventListener('click', () => {
162
- document.querySelector('#dialog1').opened = true;
163
- });
164
- </script>
165
- </template>
166
- </d2l-demo-snippet>
167
-
168
- </d2l-demo-page>
169
-
170
- </body>
171
- </html>