@openproject/primer-view-components 0.66.2 → 0.67.0-rc.87a99bede

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.
@@ -20,6 +20,8 @@ export declare class TreeViewElement extends HTMLElement {
20
20
  subTreeAtPath(path: string[]): TreeViewSubTreeNodeElement | null;
21
21
  leafAtPath(path: string[]): HTMLLIElement | null;
22
22
  getNodeCheckedValue(node: Element): TreeViewCheckedValue;
23
+ nodeHasCheckBox(node: Element): boolean;
24
+ nodeHasNativeAction(node: Element): boolean;
23
25
  infoFromNode(node: Element, newCheckedValue?: TreeViewCheckedValue): TreeViewNodeInfo | null;
24
26
  }
25
27
  declare global {
@@ -126,6 +126,12 @@ let TreeViewElement = class TreeViewElement extends HTMLElement {
126
126
  getNodeCheckedValue(node) {
127
127
  return (node.getAttribute('aria-checked') || 'false');
128
128
  }
129
+ nodeHasCheckBox(node) {
130
+ return node.querySelector('.TreeViewItemCheckbox') !== null;
131
+ }
132
+ nodeHasNativeAction(node) {
133
+ return node instanceof HTMLAnchorElement || node instanceof HTMLButtonElement;
134
+ }
129
135
  // PRIVATE API METHOD
130
136
  //
131
137
  // This would normally be marked private, but it's called by TreeViewSubTreeNodes
@@ -161,7 +167,7 @@ _TreeViewElement_nodeForEvent = function _TreeViewElement_nodeForEvent(event) {
161
167
  return node;
162
168
  };
163
169
  _TreeViewElement_handleNodeEvent = function _TreeViewElement_handleNodeEvent(node, event) {
164
- if (__classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_eventIsCheckboxToggle).call(this, event)) {
170
+ if (__classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_eventIsCheckboxToggle).call(this, event, node)) {
165
171
  __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleCheckboxToggle).call(this, node);
166
172
  }
167
173
  else if (__classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_eventIsActivation).call(this, event)) {
@@ -174,8 +180,8 @@ _TreeViewElement_handleNodeEvent = function _TreeViewElement_handleNodeEvent(nod
174
180
  __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_handleNodeKeyboardEvent).call(this, event, node);
175
181
  }
176
182
  };
177
- _TreeViewElement_eventIsCheckboxToggle = function _TreeViewElement_eventIsCheckboxToggle(event) {
178
- return event.type === 'click' && event.target.closest('.TreeViewItemCheckbox') !== null;
183
+ _TreeViewElement_eventIsCheckboxToggle = function _TreeViewElement_eventIsCheckboxToggle(event, node) {
184
+ return event.type === 'click' && this.nodeHasCheckBox(node);
179
185
  };
180
186
  _TreeViewElement_handleCheckboxToggle = function _TreeViewElement_handleCheckboxToggle(node) {
181
187
  // only handle checking of leaf nodes
@@ -190,6 +196,10 @@ _TreeViewElement_handleCheckboxToggle = function _TreeViewElement_handleCheckbox
190
196
  }
191
197
  };
192
198
  _TreeViewElement_handleNodeActivated = function _TreeViewElement_handleNodeActivated(node) {
199
+ // do not emit activation events for buttons and anchors, since it is assumed any activation
200
+ // behavior for these element types is user- or browser-defined
201
+ if (!(node instanceof HTMLDivElement))
202
+ return;
193
203
  const path = this.getNodePath(node);
194
204
  const activationSuccess = this.dispatchEvent(new CustomEvent('treeViewBeforeNodeActivated', {
195
205
  bubbles: true,
@@ -198,7 +208,10 @@ _TreeViewElement_handleNodeActivated = function _TreeViewElement_handleNodeActiv
198
208
  }));
199
209
  if (!activationSuccess)
200
210
  return;
201
- this.toggleAtPath(path);
211
+ // navigate or trigger button, don't toggle
212
+ if (!this.nodeHasNativeAction(node)) {
213
+ this.toggleAtPath(path);
214
+ }
202
215
  this.dispatchEvent(new CustomEvent('treeViewNodeActivated', {
203
216
  bubbles: true,
204
217
  detail: this.infoFromNode(node),
@@ -215,12 +228,19 @@ _TreeViewElement_handleNodeKeyboardEvent = function _TreeViewElement_handleNodeK
215
228
  }
216
229
  switch (event.key) {
217
230
  case ' ':
218
- event.preventDefault();
219
- if (this.getNodeCheckedValue(node) === 'true') {
220
- __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'false');
231
+ case 'Enter':
232
+ if (this.nodeHasCheckBox(node)) {
233
+ event.preventDefault();
234
+ if (this.getNodeCheckedValue(node) === 'true') {
235
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'false');
236
+ }
237
+ else {
238
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'true');
239
+ }
221
240
  }
222
- else {
223
- __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_setNodeCheckedValue).call(this, node, 'true');
241
+ else if (node instanceof HTMLAnchorElement) {
242
+ // simulate click on space
243
+ node.click();
224
244
  }
225
245
  break;
226
246
  }
@@ -22,12 +22,6 @@ export function useRovingTabIndex(containerEl) {
22
22
  return getNextFocusableElement(from, event) ?? from;
23
23
  },
24
24
  focusInStrategy: () => {
25
- // Don't try to execute the focusInStrategy if focus is coming from a click.
26
- // The clicked row will receive focus correctly by default.
27
- // If a chevron is clicked, setting the focus through the focuszone will prevent its toggle.
28
- // if (mouseDownRef.current) {
29
- // return undefined
30
- // }
31
25
  let currentItem = containerEl.querySelector('[aria-current]');
32
26
  currentItem = currentItem?.checkVisibility() ? currentItem : null;
33
27
  const firstItem = containerEl.querySelector('[role="treeitem"]');
@@ -110,11 +104,21 @@ function getVisibleElement(element, direction) {
110
104
  }
111
105
  let next = direction === 'next' ? walker.nextNode() : walker.previousNode();
112
106
  // If next element is nested inside a collapsed subtree, continue iterating
113
- while (next instanceof HTMLElement && next.parentElement?.closest('[role=treeitem][aria-expanded=false]')) {
107
+ while (next instanceof HTMLElement && collapsedParent(next, root)) {
114
108
  next = direction === 'next' ? walker.nextNode() : walker.previousNode();
115
109
  }
116
110
  return next instanceof HTMLElement ? next : undefined;
117
111
  }
112
+ function collapsedParent(node, root) {
113
+ for (const ancestor of root.querySelectorAll('[role=treeitem][aria-expanded=false]')) {
114
+ if (node === ancestor)
115
+ continue;
116
+ if (ancestor.closest('li')?.contains(node)) {
117
+ return ancestor;
118
+ }
119
+ }
120
+ return null;
121
+ }
118
122
  function getFirstChildElement(element) {
119
123
  const firstChild = element.querySelector('[role=treeitem]');
120
124
  return firstChild instanceof HTMLElement ? firstChild : undefined;
@@ -101,11 +101,7 @@ let TreeViewSubTreeNodeElement = class TreeViewSubTreeNodeElement extends HTMLEl
101
101
  __classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_abortController, "f").abort();
102
102
  }
103
103
  handleEvent(event) {
104
- const checkbox = event.target.closest('.TreeViewItemCheckbox');
105
- if (checkbox && checkbox === __classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_instances, "a", _TreeViewSubTreeNodeElement_checkboxElement_get)) {
106
- __classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_instances, "m", _TreeViewSubTreeNodeElement_handleCheckboxEvent).call(this, event);
107
- }
108
- else if (event.target === this.toggleButton) {
104
+ if (event.target === this.toggleButton) {
109
105
  __classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_instances, "m", _TreeViewSubTreeNodeElement_handleToggleEvent).call(this, event);
110
106
  }
111
107
  else if (event.target === this.includeFragment) {
@@ -114,6 +110,11 @@ let TreeViewSubTreeNodeElement = class TreeViewSubTreeNodeElement extends HTMLEl
114
110
  else if (event instanceof KeyboardEvent) {
115
111
  __classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_instances, "m", _TreeViewSubTreeNodeElement_handleKeyboardEvent).call(this, event);
116
112
  }
113
+ else if (event.target.closest('[role=treeitem]') === this.node &&
114
+ event.type === 'click' &&
115
+ __classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_instances, "a", _TreeViewSubTreeNodeElement_checkboxElement_get)) {
116
+ __classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_instances, "m", _TreeViewSubTreeNodeElement_handleCheckboxEvent).call(this, event);
117
+ }
117
118
  }
118
119
  expand() {
119
120
  const alreadyExpanded = this.expanded;
@@ -152,10 +153,10 @@ let TreeViewSubTreeNodeElement = class TreeViewSubTreeNodeElement extends HTMLEl
152
153
  return this.querySelectorAll(':scope > [role=treeitem]');
153
154
  }
154
155
  *eachDirectDescendantNode() {
155
- for (const leaf of this.subTree.querySelectorAll(':scope > [role=treeitem]')) {
156
+ for (const leaf of this.subTree.querySelectorAll(':scope > li > .TreeViewItemContainer > [role=treeitem]')) {
156
157
  yield leaf;
157
158
  }
158
- for (const subTree of this.subTree.querySelectorAll(':scope > tree-view-sub-tree-node > [role=treeitem]')) {
159
+ for (const subTree of this.subTree.querySelectorAll(':scope > tree-view-sub-tree-node > li > .TreeViewItemContainer > [role=treeitem]')) {
159
160
  yield subTree;
160
161
  }
161
162
  }
@@ -209,6 +210,8 @@ _TreeViewSubTreeNodeElement_instances = new WeakSet();
209
210
  _TreeViewSubTreeNodeElement_handleToggleEvent = function _TreeViewSubTreeNodeElement_handleToggleEvent(event) {
210
211
  if (event.type === 'click') {
211
212
  this.toggle();
213
+ // eslint-disable-next-line no-restricted-syntax
214
+ event.stopPropagation();
212
215
  }
213
216
  };
214
217
  _TreeViewSubTreeNodeElement_handleIncludeFragmentEvent = function _TreeViewSubTreeNodeElement_handleIncludeFragmentEvent(event) {
@@ -223,21 +226,18 @@ _TreeViewSubTreeNodeElement_handleIncludeFragmentEvent = function _TreeViewSubTr
223
226
  break;
224
227
  // request succeeded but element has not yet been replaced
225
228
  case 'include-fragment-replace':
226
- __classPrivateFieldSet(this, _TreeViewSubTreeNodeElement_activeElementIsLoader, document.activeElement === this.loadingIndicator.closest('li'), "f");
229
+ __classPrivateFieldSet(this, _TreeViewSubTreeNodeElement_activeElementIsLoader, document.activeElement === this.loadingIndicator.closest('[role=treeitem]'), "f");
227
230
  this.loadingState = 'success';
228
231
  break;
229
232
  case 'include-fragment-replaced':
230
233
  if (__classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_activeElementIsLoader, "f")) {
231
- const firstItem = this.querySelector('[role=treeitem] [role=group] > :first-child');
234
+ const firstItem = this.querySelector('[role=group] > :first-child');
232
235
  if (!firstItem)
233
236
  return;
234
- if (firstItem.tagName.toLowerCase() === 'tree-view-sub-tree-node') {
235
- const firstChild = firstItem.querySelector('[role=treeitem]');
236
- firstChild?.focus();
237
- }
238
- else {
239
- firstItem?.focus();
240
- }
237
+ const content = firstItem.querySelector('[role=treeitem]');
238
+ if (!content)
239
+ return;
240
+ content.focus();
241
241
  }
242
242
  __classPrivateFieldSet(this, _TreeViewSubTreeNodeElement_activeElementIsLoader, false, "f");
243
243
  break;
@@ -258,7 +258,13 @@ _TreeViewSubTreeNodeElement_handleKeyboardEvent = function _TreeViewSubTreeNodeE
258
258
  case 'Enter':
259
259
  // eslint-disable-next-line no-restricted-syntax
260
260
  event.stopPropagation();
261
- this.toggle();
261
+ if (__classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_instances, "a", _TreeViewSubTreeNodeElement_checkboxElement_get)) {
262
+ this.toggleChecked();
263
+ }
264
+ else if (!this.treeView?.nodeHasNativeAction(node)) {
265
+ // toggle only if this node isn't eg. an anchor or button
266
+ this.toggle();
267
+ }
262
268
  break;
263
269
  case 'ArrowRight':
264
270
  // eslint-disable-next-line no-restricted-syntax
@@ -271,10 +277,21 @@ _TreeViewSubTreeNodeElement_handleKeyboardEvent = function _TreeViewSubTreeNodeE
271
277
  this.collapse();
272
278
  break;
273
279
  case ' ':
274
- // eslint-disable-next-line no-restricted-syntax
275
- event.stopPropagation();
276
- event.preventDefault();
277
- this.toggleChecked();
280
+ if (__classPrivateFieldGet(this, _TreeViewSubTreeNodeElement_instances, "a", _TreeViewSubTreeNodeElement_checkboxElement_get)) {
281
+ // eslint-disable-next-line no-restricted-syntax
282
+ event.stopPropagation();
283
+ event.preventDefault();
284
+ this.toggleChecked();
285
+ }
286
+ else {
287
+ if (node instanceof HTMLAnchorElement) {
288
+ // simulate click on space for anchors (buttons already handle this natively)
289
+ node.click();
290
+ }
291
+ else if (!this.treeView?.nodeHasNativeAction(node)) {
292
+ this.toggle();
293
+ }
294
+ }
278
295
  break;
279
296
  }
280
297
  };
@@ -1 +1 @@
1
- .TreeViewRootUlStyles{list-style:none;margin:0;padding:0}.TreeViewRootUlStyles .TreeViewItem{outline:none}:is(.TreeViewRootUlStyles .TreeViewItem):focus-visible>div{box-shadow:var(--boxShadow-thick) var(--fgColor-accent)}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItem):focus-visible>div{outline:2px solid HighlightText;outline-offset:-2}}[data-has-leading-action]:is(.TreeViewRootUlStyles .TreeViewItem){--has-leading-action:1}.TreeViewRootUlStyles .TreeViewItemContainer{--level:1;--toggle-width:1rem;--min-item-height:2rem;border-radius:var(--borderRadius-medium);color:var(--fgColor-default);cursor:pointer;display:grid;font-size:var(--text-body-size-medium);grid-template-areas:"spacer leadingAction toggle content";grid-template-columns:var(--spacer-width) var(--leading-action-width) var(--toggle-width) 1fr;position:relative;width:100%;--leading-action-width:calc(var(--has-leading-action, 0)*1.5rem);--spacer-width:calc((var(--level) - 1)*(var(--toggle-width)/2))}:is(.TreeViewRootUlStyles .TreeViewItemContainer):hover{background-color:var(--control-transparent-bgColor-hover)}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItemContainer):hover{outline:2px solid #0000;outline-offset:-2px}}@media (pointer:coarse){.TreeViewRootUlStyles .TreeViewItemContainer{--toggle-width:1.5rem;--min-item-height:2.75rem}}:is(.TreeViewRootUlStyles .TreeViewItemContainer):has(.TreeViewFailureMessage):hover{background-color:initial;cursor:default}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItemContainer):has(.TreeViewFailureMessage):hover{outline:none}}.TreeViewRootUlStyles:where([data-omit-spacer=true]) .TreeViewItemContainer{grid-template-columns:0 0 0 1fr}.TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer{background-color:var(--control-transparent-bgColor-selected)}:is(.TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer):after{background-color:var(--fgColor-accent);border-radius:var(--borderRadius-medium);content:"";height:1.5rem;left:calc(var(--base-size-8)*-1);position:absolute;top:calc(50% - var(--base-size-12));width:.25rem}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer):after{background-color:HighlightText}}[aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox{background:var(--control-checked-bgColor-rest);border-color:var(--control-checked-borderColor-rest);transition:background-color,border-color 80ms cubic-bezier(.32,0,.67,0) 0s}:is([aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox):before{animation:checkmarkIn 80ms cubic-bezier(.65,0,.35,1) 80ms forwards;transition:visibility 0s linear 0s;visibility:visible}[aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox{background:var(--control-checked-bgColor-rest);border-color:var(--control-checked-borderColor-rest);transition:background-color,border-color 80ms cubic-bezier(.32,0,.67,0) 0s}:is([aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox):before{animation:checkmarkIn 80ms cubic-bezier(.65,0,.35,1) 80ms forwards;clip-path:none;mask-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMCIgaGVpZ2h0PSIyIiBmaWxsPSJub25lIiB2aWV3Qm94PSIwIDAgMTAgMiI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMCAxYTEgMSAwIDAgMSAxLTFoOGExIDEgMCAxIDEgMCAySDFhMSAxIDAgMCAxLTEtMSIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+");visibility:visible}.TreeViewRootUlStyles .TreeViewItemToggle{align-items:flex-start;color:var(--fgColor-muted);display:flex;grid-area:toggle;height:100%;justify-content:center;padding-top:calc(var(--min-item-height)/2 - var(--base-size-12)/2)}.TreeViewRootUlStyles .TreeViewItemToggleHover:hover{background-color:var(--control-transparent-bgColor-hover)}.TreeViewRootUlStyles .TreeViewItemToggleEnd{border-bottom-left-radius:var(--borderRadius-medium);border-top-left-radius:var(--borderRadius-medium)}.TreeViewRootUlStyles .TreeViewItemContent{display:flex;gap:var(--stack-gap-condensed);grid-area:content;height:100%;line-height:var(--custom-line-height,var(--text-body-lineHeight-medium,1.4285));padding:0 var(--base-size-8);padding-bottom:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2);padding-top:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2)}:is(.TreeViewRootUlStyles .TreeViewItemContent) .TreeViewItemCheckbox{background-color:initial;border:none;border-radius:var(--borderRadius-medium);color:var(--control-fgColor-rest);position:relative;text-align:left;touch-action:manipulation;transition:background 33.333ms linear;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.TreeViewRootUlStyles .TreeViewItemContentText{flex:1 1 auto;width:0}.TreeViewRootUlStyles:where([data-truncate-text=true]) .TreeViewItemContentText{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.TreeViewRootUlStyles:where([data-truncate-text=false]) .TreeViewItemContentText{word-break:break-word}.TreeViewRootUlStyles .TreeViewItemVisual{align-items:center;color:var(--fgColor-muted);display:flex;height:var(--custom-line-height,1.3rem)}.TreeViewRootUlStyles .TreeViewItemLeadingAction{color:var(--fgColor-muted);display:flex;grid-area:leadingAction}:is(.TreeViewRootUlStyles .TreeViewItemLeadingAction)>button{flex-shrink:1}.TreeViewRootUlStyles .TreeViewItemLevelLine{border-color:var(--borderColor-muted);border-right:var(--borderWidth-thin) solid;height:100%;width:100%}@media (hover:hover){.TreeViewRootUlStyles .TreeViewItemLevelLine{border-color:#0000}.TreeViewRootUlStyles:focus-within .TreeViewItemLevelLine,.TreeViewRootUlStyles:hover .TreeViewItemLevelLine{border-color:var(--borderColor-muted)}}.TreeViewRootUlStyles .TreeViewVisuallyHidden{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border-width:0;white-space:nowrap}.TreeViewSkeletonItemContainerStyle{align-items:center;column-gap:.5rem;display:flex;height:2rem}@media (pointer:coarse){.TreeViewSkeletonItemContainerStyle{height:2.75rem}}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+1){--tree-item-loading-width:67%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+2){--tree-item-loading-width:47%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+3){--tree-item-loading-width:73%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+4){--tree-item-loading-width:64%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+5){--tree-item-loading-width:50%}.TreeItemSkeletonTextStyles{width:var(--tree-item-loading-width,67%)}.TreeViewFailureMessage{align-items:center;display:grid;gap:.5rem;grid-template-columns:auto 1fr;width:100%}
1
+ .TreeViewRootUlStyles{list-style:none;margin:0;padding:0}.TreeViewRootUlStyles .TreeViewItem{outline:none}:is(.TreeViewRootUlStyles .TreeViewItem):focus-visible>div{box-shadow:var(--boxShadow-thick) var(--fgColor-accent)}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItem):focus-visible>div{outline:2px solid HighlightText;outline-offset:-2}}[data-has-leading-action]:is(.TreeViewRootUlStyles .TreeViewItem){--has-leading-action:1}.TreeViewRootUlStyles .TreeViewItemContainer{--level:1;--toggle-width:1rem;--min-item-height:2rem;border-radius:var(--borderRadius-medium);color:var(--fgColor-default);display:grid;font-size:var(--text-body-size-medium);grid-template-areas:"spacer leadingAction toggle content";grid-template-columns:var(--spacer-width) var(--leading-action-width) var(--toggle-width) 1fr;position:relative;width:100%;--leading-action-width:calc(var(--has-leading-action, 0)*1.5rem);--spacer-width:calc((var(--level) - 1)*(var(--toggle-width)/2))}:is(.TreeViewRootUlStyles .TreeViewItemContainer):hover{background-color:var(--control-transparent-bgColor-hover)}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItemContainer):hover{outline:2px solid #0000;outline-offset:-2px}}@media (pointer:coarse){.TreeViewRootUlStyles .TreeViewItemContainer{--toggle-width:1.5rem;--min-item-height:2.75rem}}:is(.TreeViewRootUlStyles .TreeViewItemContainer):has(.TreeViewFailureMessage):hover{background-color:initial;cursor:default}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItemContainer):has(.TreeViewFailureMessage):hover{outline:none}}:is(.TreeViewRootUlStyles .TreeViewItemContainer):has([role=treeitem]:focus-visible){box-shadow:var(--boxShadow-thick) var(--fgColor-accent)}.TreeViewRootUlStyles:where([data-omit-spacer=true]) .TreeViewItemContainer{grid-template-columns:0 0 0 1fr}.TreeViewRootUlStyles .TreeViewItem>.TreeViewItemContainer:has(.TreeViewItemContent[aria-current=true]){background-color:var(--control-transparent-bgColor-selected)}:is(.TreeViewRootUlStyles .TreeViewItem>.TreeViewItemContainer:has(.TreeViewItemContent[aria-current=true])):after{background-color:var(--fgColor-accent);border-radius:var(--borderRadius-medium);content:"";height:1.5rem;left:calc(var(--base-size-8)*-1);position:absolute;top:calc(50% - var(--base-size-12));width:.25rem}@media (forced-colors:active){:is(.TreeViewRootUlStyles .TreeViewItem>.TreeViewItemContainer:has(.TreeViewItemContent[aria-current=true])):after{background-color:HighlightText}}.TreeViewRootUlStyles .TreeViewItemToggle{align-items:flex-start;color:var(--fgColor-muted);cursor:pointer;display:flex;grid-area:toggle;height:100%;justify-content:center;padding-top:calc(var(--min-item-height)/2 - var(--base-size-12)/2)}.TreeViewRootUlStyles .TreeViewItemToggleHover:hover{background-color:var(--control-transparent-bgColor-hover)}.TreeViewRootUlStyles .TreeViewItemToggleEnd{border-bottom-left-radius:var(--borderRadius-medium);border-top-left-radius:var(--borderRadius-medium)}.TreeViewRootUlStyles a.TreeViewItemContent:hover,.TreeViewRootUlStyles button.TreeViewItemContent:hover{-webkit-text-decoration:underline;text-decoration:underline;text-decoration-color:var(--control-fgColor-rest)}.TreeViewRootUlStyles .TreeViewItemContent{cursor:pointer;display:flex;gap:var(--stack-gap-condensed);grid-area:content;height:100%;line-height:var(--custom-line-height,var(--text-body-lineHeight-medium,1.4285));outline:none;padding:0 var(--base-size-8);padding-bottom:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2);padding-top:calc((var(--min-item-height) - var(--custom-line-height, 1.3rem))/2)}.TreeViewRootUlStyles .TreeViewItemContent,:is(.TreeViewRootUlStyles .TreeViewItemContent) .TreeViewItemCheckbox{background-color:initial;border:none;text-align:left;touch-action:manipulation;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}:is(.TreeViewRootUlStyles .TreeViewItemContent) .TreeViewItemCheckbox{border-radius:var(--borderRadius-medium);color:var(--control-fgColor-rest);position:relative;transition:background 33.333ms linear}[aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItemContent) .FormControl-checkbox{background:var(--control-checked-bgColor-rest);border-color:var(--control-checked-borderColor-rest);transition:background-color,border-color 80ms cubic-bezier(.32,0,.67,0) 0s}:is([aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItemContent) .FormControl-checkbox):before{animation:checkmarkIn 80ms cubic-bezier(.65,0,.35,1) 80ms forwards;transition:visibility 0s linear 0s;visibility:visible}[aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItemContent) .FormControl-checkbox{background:var(--control-checked-bgColor-rest);border-color:var(--control-checked-borderColor-rest);transition:background-color,border-color 80ms cubic-bezier(.32,0,.67,0) 0s}:is([aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItemContent) .FormControl-checkbox):before{animation:checkmarkIn 80ms cubic-bezier(.65,0,.35,1) 80ms forwards;clip-path:none;mask-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMCIgaGVpZ2h0PSIyIiBmaWxsPSJub25lIiB2aWV3Qm94PSIwIDAgMTAgMiI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMCAxYTEgMSAwIDAgMSAxLTFoOGExIDEgMCAxIDEgMCAySDFhMSAxIDAgMCAxLTEtMSIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+");visibility:visible}.TreeViewRootUlStyles .TreeViewItemContentText{color:var(--control-fgColor-rest);flex:1 1 auto;width:0}.TreeViewRootUlStyles:where([data-truncate-text=true]) .TreeViewItemContentText{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.TreeViewRootUlStyles:where([data-truncate-text=false]) .TreeViewItemContentText{word-break:break-word}.TreeViewRootUlStyles .TreeViewItemVisual{align-items:center;color:var(--fgColor-muted);display:flex;height:var(--custom-line-height,1.3rem)}.TreeViewRootUlStyles .TreeViewItemLeadingAction{color:var(--fgColor-muted);display:flex;grid-area:leadingAction}:is(.TreeViewRootUlStyles .TreeViewItemLeadingAction)>button{flex-shrink:1}.TreeViewRootUlStyles .TreeViewItemLevelLine{border-color:var(--borderColor-muted);border-right:var(--borderWidth-thin) solid;height:100%;width:100%}@media (hover:hover){.TreeViewRootUlStyles .TreeViewItemLevelLine{border-color:#0000}.TreeViewRootUlStyles:focus-within .TreeViewItemLevelLine,.TreeViewRootUlStyles:hover .TreeViewItemLevelLine{border-color:var(--borderColor-muted)}}.TreeViewRootUlStyles .TreeViewVisuallyHidden{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border-width:0;white-space:nowrap}.TreeViewSkeletonItemContainerStyle{align-items:center;column-gap:.5rem;display:flex;height:2rem}@media (pointer:coarse){.TreeViewSkeletonItemContainerStyle{height:2.75rem}}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+1){--tree-item-loading-width:67%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+2){--tree-item-loading-width:47%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+3){--tree-item-loading-width:73%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+4){--tree-item-loading-width:64%}.TreeViewSkeletonItemContainerStyle:nth-of-type(5n+5){--tree-item-loading-width:50%}.TreeItemSkeletonTextStyles{width:var(--tree-item-loading-width,67%)}.TreeViewFailureMessage{align-items:center;display:grid;gap:.5rem;grid-template-columns:auto 1fr;width:100%}
@@ -8,18 +8,21 @@
8
8
  ".TreeViewRootUlStyles .TreeViewItemContainer",
9
9
  ":is(.TreeViewRootUlStyles .TreeViewItemContainer):hover",
10
10
  ":is(.TreeViewRootUlStyles .TreeViewItemContainer):has(.TreeViewFailureMessage):hover",
11
+ ":is(.TreeViewRootUlStyles .TreeViewItemContainer):has([role=treeitem]:focus-visible)",
11
12
  ".TreeViewRootUlStyles:where([data-omit-spacer=true]) .TreeViewItemContainer",
12
- ".TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer",
13
- ":is(.TreeViewRootUlStyles .TreeViewItem[aria-current=true]>.TreeViewItemContainer):after",
14
- "[aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox",
15
- ":is([aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox):before",
16
- "[aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox",
17
- ":is([aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItem)>.TreeViewItemContainer .FormControl-checkbox):before",
13
+ ".TreeViewRootUlStyles .TreeViewItem>.TreeViewItemContainer:has(.TreeViewItemContent[aria-current=true])",
14
+ ":is(.TreeViewRootUlStyles .TreeViewItem>.TreeViewItemContainer:has(.TreeViewItemContent[aria-current=true])):after",
18
15
  ".TreeViewRootUlStyles .TreeViewItemToggle",
19
16
  ".TreeViewRootUlStyles .TreeViewItemToggleHover:hover",
20
17
  ".TreeViewRootUlStyles .TreeViewItemToggleEnd",
18
+ ".TreeViewRootUlStyles a.TreeViewItemContent:hover",
19
+ ".TreeViewRootUlStyles button.TreeViewItemContent:hover",
21
20
  ".TreeViewRootUlStyles .TreeViewItemContent",
22
21
  ":is(.TreeViewRootUlStyles .TreeViewItemContent) .TreeViewItemCheckbox",
22
+ "[aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItemContent) .FormControl-checkbox",
23
+ ":is([aria-checked=true]:is(.TreeViewRootUlStyles .TreeViewItemContent) .FormControl-checkbox):before",
24
+ "[aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItemContent) .FormControl-checkbox",
25
+ ":is([aria-checked=mixed]:is(.TreeViewRootUlStyles .TreeViewItemContent) .FormControl-checkbox):before",
23
26
  ".TreeViewRootUlStyles .TreeViewItemContentText",
24
27
  ".TreeViewRootUlStyles:where([data-truncate-text=true]) .TreeViewItemContentText",
25
28
  ".TreeViewRootUlStyles:where([data-truncate-text=false]) .TreeViewItemContentText",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openproject/primer-view-components",
3
- "version": "0.66.2",
3
+ "version": "0.67.0-rc.87a99bede",
4
4
  "description": "ViewComponents of the Primer Design System for OpenProject",
5
5
  "main": "app/assets/javascripts/primer_view_components.js",
6
6
  "module": "app/components/primer/primer.js",
@@ -5419,6 +5419,12 @@
5419
5419
  "source": "https://github.com/primer/view_components/tree/main/app/components/primer/open_project/file_tree_view.rb",
5420
5420
  "lookbook": "https://primer.style/view-components/lookbook/inspect/primer/open_project/file_tree_view/default/",
5421
5421
  "parameters": [
5422
+ {
5423
+ "name": "node_variant",
5424
+ "type": "Symbol",
5425
+ "default": "`:div`",
5426
+ "description": "The variant to use for this node. One of `:anchor`, `:button`, or `:div`."
5427
+ },
5422
5428
  {
5423
5429
  "name": "system_arguments",
5424
5430
  "type": "Hash",
@@ -5844,6 +5850,12 @@
5844
5850
  "source": "https://github.com/primer/view_components/tree/main/app/components/primer/open_project/tree_view.rb",
5845
5851
  "lookbook": "https://primer.style/view-components/lookbook/inspect/primer/open_project/tree_view/default/",
5846
5852
  "parameters": [
5853
+ {
5854
+ "name": "node_variant",
5855
+ "type": "Symbol",
5856
+ "default": "`:div`",
5857
+ "description": "The variant to use for this node. One of `:anchor`, `:button`, or `:div`."
5858
+ },
5847
5859
  {
5848
5860
  "name": "system_arguments",
5849
5861
  "type": "Hash",
@@ -5970,6 +5982,18 @@
5970
5982
  "default": "N/A",
5971
5983
  "description": "The node's \"path,\" i.e. this node's label and the labels of all its ancestors. This node should be reachable by traversing the tree following this path."
5972
5984
  },
5985
+ {
5986
+ "name": "node_variant",
5987
+ "type": "Symbol",
5988
+ "default": "N/A",
5989
+ "description": "The node variant to use for the node's content, i.e. the `:button` or `:div`. One of `:anchor`, `:button`, or `:div`."
5990
+ },
5991
+ {
5992
+ "name": "href",
5993
+ "type": "String",
5994
+ "default": "`nil`",
5995
+ "description": "The URL to use as the `href` attribute for this node. If set to a truthy value, the `tag:` parameter is ignored and assumed to be `:a`."
5996
+ },
5973
5997
  {
5974
5998
  "name": "current",
5975
5999
  "type": "Boolean",
@@ -5989,10 +6013,10 @@
5989
6013
  "description": "The checked state of the node's checkbox. One of `false`, `mixed`, or `true`."
5990
6014
  },
5991
6015
  {
5992
- "name": "system_arguments",
6016
+ "name": "content_arguments",
5993
6017
  "type": "Hash",
5994
6018
  "default": "N/A",
5995
- "description": "The arguments accepted by [ActionList](/components/alpha/actionlist)."
6019
+ "description": "Arguments attached to the node's content, i.e the `<button>` or `<a>` element. [System arguments](/system-arguments)"
5996
6020
  }
5997
6021
  ]
5998
6022
  },
@@ -6054,6 +6078,12 @@
6054
6078
  "source": "https://github.com/primer/view_components/tree/main/app/components/primer/open_project/tree_view/sub_tree.rb",
6055
6079
  "lookbook": "https://primer.style/view-components/lookbook/inspect/primer/open_project/tree_view/sub_tree/default/",
6056
6080
  "parameters": [
6081
+ {
6082
+ "name": "node_variant",
6083
+ "type": "Symbol",
6084
+ "default": "N/A",
6085
+ "description": "The variant to use for this node. One of `:anchor`, `:button`, or `:div`."
6086
+ },
6057
6087
  {
6058
6088
  "name": "system_arguments",
6059
6089
  "type": "Hash",
@@ -6110,6 +6140,12 @@
6110
6140
  "default": "N/A",
6111
6141
  "description": "The node's \"path,\" i.e. this node's label and the labels of all its ancestors. This node should be reachable by traversing the tree following this path."
6112
6142
  },
6143
+ {
6144
+ "name": "node_variant",
6145
+ "type": "Symbol",
6146
+ "default": "N/A",
6147
+ "description": "The variant to use for this node. One of `:anchor`, `:button`, or `:div`."
6148
+ },
6113
6149
  {
6114
6150
  "name": "expanded",
6115
6151
  "type": "Boolean",
@@ -1786,12 +1786,18 @@
1786
1786
  "GeneratedSlotMethods": "Primer::OpenProject::SubHeader::SegmentedControl::GeneratedSlotMethods"
1787
1787
  },
1788
1788
  "Primer::OpenProject::TreeView": {
1789
+ "DEFAULT_NODE_VARIANT": "div",
1789
1790
  "GeneratedSlotMethods": "Primer::OpenProject::TreeView::GeneratedSlotMethods",
1790
1791
  "Icon": "Primer::OpenProject::TreeView::Icon",
1791
1792
  "IconPair": "Primer::OpenProject::TreeView::IconPair",
1792
1793
  "LeadingAction": "Primer::OpenProject::TreeView::LeadingAction",
1793
1794
  "LeafNode": "Primer::OpenProject::TreeView::LeafNode",
1794
1795
  "LoadingFailureMessage": "Primer::OpenProject::TreeView::LoadingFailureMessage",
1796
+ "NODE_VARIANT_OPTIONS": [
1797
+ "div",
1798
+ "anchor",
1799
+ "button"
1800
+ ],
1795
1801
  "Node": "Primer::OpenProject::TreeView::Node",
1796
1802
  "SkeletonLoader": "Primer::OpenProject::TreeView::SkeletonLoader",
1797
1803
  "SpinnerLoader": "Primer::OpenProject::TreeView::SpinnerLoader",
@@ -1824,8 +1830,19 @@
1824
1830
  "mixed"
1825
1831
  ],
1826
1832
  "DEFAULT_CHECKED_STATE": false,
1833
+ "DEFAULT_NODE_VARIANT": "div",
1827
1834
  "DEFAULT_SELECT_VARIANT": "none",
1828
1835
  "GeneratedSlotMethods": "Primer::OpenProject::TreeView::Node::GeneratedSlotMethods",
1836
+ "NODE_VARIANT_TAG_MAP": {
1837
+ "div": "div",
1838
+ "button": "button",
1839
+ "anchor": "a"
1840
+ },
1841
+ "NODE_VARIANT_TAG_OPTIONS": [
1842
+ "div",
1843
+ "button",
1844
+ "anchor"
1845
+ ],
1829
1846
  "SELECT_VARIANT_OPTIONS": [
1830
1847
  "multiple",
1831
1848
  "none"