@salesforcedevs/dx-components 1.40.4 → 1.40.5-final-alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lwc.config.json +1 -0
- package/package.json +46 -47
- package/src/modules/dx/button/button.css +2 -2
- package/src/modules/dx/dropdown/dropdown.css +29 -1
- package/src/modules/dx/dropdown/dropdown.html +1 -1
- package/src/modules/dx/dropdown/dropdown.ts +16 -1
- package/src/modules/dx/dropdownOption/dropdownOption.css +32 -6
- package/src/modules/dx/popover/popover.ts +119 -7
- package/src/modules/dx/tree/treeHandler.ts +82 -10
- package/src/modules/dx/treeItem/treeItem.css +30 -0
- package/src/modules/dx/treeItem/treeItem.html +16 -2
- package/src/modules/dx/treeItem/treeItem.ts +99 -10
- package/src/modules/dx/treeTile/treeTile.css +4 -0
- package/src/modules/dx/treeTile/treeTile.ts +12 -3
- package/src/modules/dx/typeBadge/typeBadge.ts +23 -2
- package/src/modules/dxBaseElements/headerBase/headerBase.ts +2 -1
- package/src/modules/dxConstants/brands/brands.ts +2 -1
- package/src/modules/dxHelpers/commonSidebar/commonSidebar.css +1 -1
- package/src/modules/dxHelpers/commonTreeItem/commonTreeItem.css +4 -0
- package/src/modules/dxHelpers/typeBadge/typeBadge.css +22 -0
- package/src/modules/dxUtils/topicVersion/topicVersion.ts +476 -0
- package/LICENSE +0 -12
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<div class={rowClass}>
|
|
3
3
|
<dx-tree-tile
|
|
4
4
|
href={href}
|
|
5
5
|
is-root={isRoot}
|
|
@@ -7,12 +7,26 @@
|
|
|
7
7
|
is-expanded={isExpanded}
|
|
8
8
|
has-children={hasChildren}
|
|
9
9
|
is-selected={isSelected}
|
|
10
|
+
is-contained-selection={isContainedSelection}
|
|
10
11
|
oniconclick={onIconClick}
|
|
11
12
|
show-arrow={isParent}
|
|
12
13
|
target={target}
|
|
13
14
|
is-external-link={isExternalLink}
|
|
15
|
+
has-version-picker={showVersionPicker}
|
|
16
|
+
onclick={onLinkClick}
|
|
14
17
|
></dx-tree-tile>
|
|
15
|
-
|
|
18
|
+
<doc-version-picker
|
|
19
|
+
if:true={showVersionPicker}
|
|
20
|
+
class="tree-item-version-picker"
|
|
21
|
+
small="true"
|
|
22
|
+
versions={versionPickerOptions}
|
|
23
|
+
selected-version={selectedVersion}
|
|
24
|
+
latest-version={isLatestVersion}
|
|
25
|
+
onchange={onVersionChange}
|
|
26
|
+
onclick={onVersionPickerClick}
|
|
27
|
+
onmousedown={onVersionPickerClick}
|
|
28
|
+
></doc-version-picker>
|
|
29
|
+
</div>
|
|
16
30
|
<img
|
|
17
31
|
if:true={showChildrenLoading}
|
|
18
32
|
class="tree-children-loading"
|
|
@@ -2,9 +2,24 @@ import { LightningElement, api } from "lwc";
|
|
|
2
2
|
import { InternalTreeNode } from "typings/custom";
|
|
3
3
|
import { track } from "dxUtils/analytics";
|
|
4
4
|
import { isInViewport } from "dxUtils/browser";
|
|
5
|
+
import type { TopicVersionOption } from "dxUtils/topicVersion";
|
|
6
|
+
import {
|
|
7
|
+
firstContentNode,
|
|
8
|
+
getTopicVersionKey,
|
|
9
|
+
getVersionPickerOptions,
|
|
10
|
+
isParentOnlyTopic,
|
|
11
|
+
normalizePath,
|
|
12
|
+
persistTopicVersion,
|
|
13
|
+
resolveSelectedVersion,
|
|
14
|
+
shouldShowTopicVersionPicker
|
|
15
|
+
} from "dxUtils/topicVersion";
|
|
5
16
|
|
|
6
17
|
const DEFAULT_TARGET = "_self";
|
|
7
18
|
|
|
19
|
+
type VersionedInternalTreeNode = InternalTreeNode & {
|
|
20
|
+
versions?: TopicVersionOption[];
|
|
21
|
+
};
|
|
22
|
+
|
|
8
23
|
export default class TreeItem extends LightningElement {
|
|
9
24
|
@api selectedKey?: string;
|
|
10
25
|
@api isRoot: boolean = false;
|
|
@@ -20,7 +35,7 @@ export default class TreeItem extends LightningElement {
|
|
|
20
35
|
this.isChildrenLoading = (node && node.isChildrenLoading) || false;
|
|
21
36
|
}
|
|
22
37
|
|
|
23
|
-
private _treeNode!:
|
|
38
|
+
private _treeNode!: VersionedInternalTreeNode;
|
|
24
39
|
private isExpanded: boolean = false;
|
|
25
40
|
private isChildrenLoading: boolean = false;
|
|
26
41
|
private rootParentLabel: string = "";
|
|
@@ -30,9 +45,22 @@ export default class TreeItem extends LightningElement {
|
|
|
30
45
|
}
|
|
31
46
|
|
|
32
47
|
private get isSelected(): boolean {
|
|
48
|
+
if (isParentOnlyTopic(this._treeNode)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
33
51
|
return this._treeNode.key === this.selectedKey && !this.isExternalLink;
|
|
34
52
|
}
|
|
35
53
|
|
|
54
|
+
private get rowClass(): string {
|
|
55
|
+
return this.isSelected && this.showVersionPicker
|
|
56
|
+
? "tree-item-row tree-item-row-selected"
|
|
57
|
+
: "tree-item-row";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private get isContainedSelection(): boolean {
|
|
61
|
+
return this.isSelected && this.showVersionPicker;
|
|
62
|
+
}
|
|
63
|
+
|
|
36
64
|
private get hasChildren(): boolean {
|
|
37
65
|
return !!(
|
|
38
66
|
this.treeNode &&
|
|
@@ -83,8 +111,44 @@ export default class TreeItem extends LightningElement {
|
|
|
83
111
|
return `${parentLabels.reverse().join(":")}:${this.treeNode.label}`;
|
|
84
112
|
}
|
|
85
113
|
|
|
114
|
+
private get versions(): TopicVersionOption[] | undefined {
|
|
115
|
+
return this._treeNode?.versions;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private get versionPickerOptions(): TopicVersionOption[] | undefined {
|
|
119
|
+
return getVersionPickerOptions(this._treeNode);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private get showVersionPicker(): boolean {
|
|
123
|
+
return shouldShowTopicVersionPicker(
|
|
124
|
+
this.versions,
|
|
125
|
+
this._treeNode?.showForwardArrow
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private get selectedVersion(): TopicVersionOption | undefined {
|
|
130
|
+
return resolveSelectedVersion(this._treeNode);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private get isLatestVersion(): boolean {
|
|
134
|
+
return (
|
|
135
|
+
!!this.selectedVersion &&
|
|
136
|
+
this.selectedVersion.id === this.versions?.[0]?.id
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
86
140
|
private get href(): string | undefined {
|
|
141
|
+
if (isParentOnlyTopic(this._treeNode)) {
|
|
142
|
+
const contentNode = firstContentNode(this._treeNode);
|
|
143
|
+
return (
|
|
144
|
+
contentNode?.link?.href ||
|
|
145
|
+
contentNode?.name ||
|
|
146
|
+
this.selectedVersion?.link?.href ||
|
|
147
|
+
""
|
|
148
|
+
);
|
|
149
|
+
}
|
|
87
150
|
return (
|
|
151
|
+
this.selectedVersion?.link?.href ||
|
|
88
152
|
(this._treeNode.link && this._treeNode.link.href) ||
|
|
89
153
|
this._treeNode.name ||
|
|
90
154
|
""
|
|
@@ -104,6 +168,23 @@ export default class TreeItem extends LightningElement {
|
|
|
104
168
|
setExpanded: this.setExpanded.bind(this)
|
|
105
169
|
});
|
|
106
170
|
}
|
|
171
|
+
|
|
172
|
+
// Remember this topic's resolved version (own URL, descendant URL, or
|
|
173
|
+
// session) so sibling pickers stay independent for the tab.
|
|
174
|
+
const selected = resolveSelectedVersion(this._treeNode);
|
|
175
|
+
const topicKey = getTopicVersionKey(this._treeNode);
|
|
176
|
+
if (this.versions?.length && selected && topicKey) {
|
|
177
|
+
persistTopicVersion(topicKey, selected.id);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private onVersionChange(event: CustomEvent): void {
|
|
182
|
+
event.stopPropagation();
|
|
183
|
+
persistTopicVersion(getTopicVersionKey(this._treeNode), event.detail);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private onVersionPickerClick(event: Event): void {
|
|
187
|
+
event.stopPropagation();
|
|
107
188
|
}
|
|
108
189
|
|
|
109
190
|
renderedCallback(): void {
|
|
@@ -134,8 +215,8 @@ export default class TreeItem extends LightningElement {
|
|
|
134
215
|
private preventDefaultLinkBehavior(event: Event) {
|
|
135
216
|
// prevent page refresh if href isn't present or link is already active
|
|
136
217
|
if (
|
|
137
|
-
!this.
|
|
138
|
-
this.
|
|
218
|
+
!this.href ||
|
|
219
|
+
normalizePath(this.href) === normalizePath(window.location.pathname)
|
|
139
220
|
) {
|
|
140
221
|
event.preventDefault();
|
|
141
222
|
}
|
|
@@ -155,10 +236,14 @@ export default class TreeItem extends LightningElement {
|
|
|
155
236
|
this.sendGtm(event);
|
|
156
237
|
return;
|
|
157
238
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
this.
|
|
161
|
-
|
|
239
|
+
// No copied destination. A markdown descendant is selected below.
|
|
240
|
+
// A chain with no content page only expands.
|
|
241
|
+
if (!firstContentNode(this._treeNode)?.link?.href) {
|
|
242
|
+
const isSelectAction = false;
|
|
243
|
+
this.doExpand(isSelectAction);
|
|
244
|
+
this.sendGtm(event);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
162
247
|
}
|
|
163
248
|
|
|
164
249
|
if (this.isParent) {
|
|
@@ -167,8 +252,12 @@ export default class TreeItem extends LightningElement {
|
|
|
167
252
|
}
|
|
168
253
|
|
|
169
254
|
this.sendGtm(event);
|
|
255
|
+
const contentNode = isParentOnlyTopic(this._treeNode)
|
|
256
|
+
? firstContentNode(this._treeNode)
|
|
257
|
+
: undefined;
|
|
170
258
|
this.fireEvent("tileselected", {
|
|
171
|
-
name: this._treeNode.name,
|
|
259
|
+
name: contentNode?.name || this._treeNode.name,
|
|
260
|
+
...(contentNode?.key ? { key: contentNode.key } : {}),
|
|
172
261
|
isExternalLink: this.isExternalLink
|
|
173
262
|
});
|
|
174
263
|
}
|
|
@@ -186,8 +275,8 @@ export default class TreeItem extends LightningElement {
|
|
|
186
275
|
this.dispatchEvent(
|
|
187
276
|
new CustomEvent(type, {
|
|
188
277
|
detail: {
|
|
189
|
-
|
|
190
|
-
|
|
278
|
+
key: this._treeNode.key,
|
|
279
|
+
...detail
|
|
191
280
|
},
|
|
192
281
|
bubbles: true,
|
|
193
282
|
composed: true
|
|
@@ -28,10 +28,12 @@ export default class TreeTile extends LightningElement {
|
|
|
28
28
|
@api isExpanded = false;
|
|
29
29
|
@api hasChildren = false;
|
|
30
30
|
@api isSelected = false;
|
|
31
|
+
@api isContainedSelection = false;
|
|
31
32
|
@api href!: string;
|
|
32
33
|
@api showArrow = false;
|
|
33
34
|
@api target!: string;
|
|
34
35
|
@api isExternalLink = false;
|
|
36
|
+
@api hasVersionPicker = false;
|
|
35
37
|
|
|
36
38
|
get tileAriaLevel() {
|
|
37
39
|
return this.treeNode.level + 2;
|
|
@@ -43,18 +45,21 @@ export default class TreeTile extends LightningElement {
|
|
|
43
45
|
* screen reader users (WCAG 2.4.4)
|
|
44
46
|
*/
|
|
45
47
|
get linkAriaLabel(): string | null {
|
|
46
|
-
if (!this.treeNode.redirectToChild
|
|
48
|
+
if (!this.treeNode.redirectToChild) {
|
|
47
49
|
return null;
|
|
48
50
|
}
|
|
49
51
|
/**
|
|
50
52
|
* The redirect can target a deep descendant, so walk the first-child
|
|
51
53
|
* chain to the node that owns the destination link and name it.
|
|
54
|
+
* The destination href may live on this anchor (parent-only topics do
|
|
55
|
+
* not copy it onto the parent node).
|
|
52
56
|
*/
|
|
53
57
|
let destination = this.treeNode.children?.[0];
|
|
54
58
|
while (destination?.redirectToChild) {
|
|
55
59
|
destination = destination.children?.[0];
|
|
56
60
|
}
|
|
57
|
-
|
|
61
|
+
const triggerHref = this.href || this.treeNode.link?.href;
|
|
62
|
+
return destination?.link?.href && destination.link.href === triggerHref
|
|
58
63
|
? `${this.treeNode.label}, opens ${destination.label}`
|
|
59
64
|
: null;
|
|
60
65
|
}
|
|
@@ -101,7 +106,11 @@ export default class TreeTile extends LightningElement {
|
|
|
101
106
|
"tile-root": this.isRoot,
|
|
102
107
|
"tile-with-children": !this.isRoot && this.hasChildren,
|
|
103
108
|
"tile-root-with-child": this.isRoot && this.showArrow,
|
|
104
|
-
"sidebar-item-selected":
|
|
109
|
+
"sidebar-item-selected":
|
|
110
|
+
this.isSelected && !this.isContainedSelection,
|
|
111
|
+
"sidebar-item-selected-text":
|
|
112
|
+
this.isSelected && this.isContainedSelection,
|
|
113
|
+
"tile-with-version-picker": this.hasVersionPicker
|
|
105
114
|
});
|
|
106
115
|
}
|
|
107
116
|
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
IconSprite,
|
|
8
8
|
IconData,
|
|
9
9
|
IconSymbol,
|
|
10
|
+
StatusBadgeVariant,
|
|
10
11
|
TypeBadgeSize
|
|
11
12
|
} from "typings/custom";
|
|
12
13
|
import { colorToDxColors, buildStyleColorVariables } from "dxUtils/css";
|
|
@@ -20,9 +21,16 @@ import {
|
|
|
20
21
|
getContentTypeColorVariables
|
|
21
22
|
} from "dxUtils/contentTypes";
|
|
22
23
|
|
|
24
|
+
// Opt-in solid status variants (status-success/-warning/-error).
|
|
25
|
+
export const STATUS_BADGE_VARIANTS: StatusBadgeVariant[] = [
|
|
26
|
+
"status-success",
|
|
27
|
+
"status-warning",
|
|
28
|
+
"status-error"
|
|
29
|
+
];
|
|
30
|
+
|
|
23
31
|
export default class TypeBadge extends LightningElement {
|
|
24
32
|
@api size: TypeBadgeSize = "default";
|
|
25
|
-
@api variant: Brand | ContentType = "default";
|
|
33
|
+
@api variant: Brand | ContentType | StatusBadgeVariant = "default";
|
|
26
34
|
@api color?: Color;
|
|
27
35
|
@api dark?: false;
|
|
28
36
|
|
|
@@ -87,11 +95,24 @@ export default class TypeBadge extends LightningElement {
|
|
|
87
95
|
return isContentType(this.variant);
|
|
88
96
|
}
|
|
89
97
|
|
|
98
|
+
private get isStatus(): boolean {
|
|
99
|
+
return STATUS_BADGE_VARIANTS.includes(
|
|
100
|
+
this.variant as StatusBadgeVariant
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
90
104
|
private get className(): string {
|
|
91
|
-
return cx(
|
|
105
|
+
return cx(
|
|
106
|
+
"dx-badge",
|
|
107
|
+
`size-${this.size}`,
|
|
108
|
+
this.isStatus && this.variant
|
|
109
|
+
);
|
|
92
110
|
}
|
|
93
111
|
|
|
94
112
|
private get style(): string {
|
|
113
|
+
if (this.isStatus) {
|
|
114
|
+
return "";
|
|
115
|
+
}
|
|
95
116
|
if (this.dark) {
|
|
96
117
|
return buildStyleColorVariables({
|
|
97
118
|
background: "--dx-g-purple-vibrant-40",
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
margin-bottom: var(--dx-g-spacing-2xl);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
@media screen and (max-width:
|
|
173
|
+
@media screen and (max-width: 768px) {
|
|
174
174
|
:host {
|
|
175
175
|
--doc-version-picker-width: calc(
|
|
176
176
|
100vw - var(--dx-c-sidebar-left-padding) - var(--dx-g-spacing-lg)
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
color: var(--dx-g-blue-vibrant-40) !important;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
.sidebar-item-selected-text {
|
|
17
|
+
color: var(--dx-g-blue-vibrant-40) !important;
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
.sidebar-item {
|
|
17
21
|
padding: 6px var(--dx-g-spacing-lg) 6px
|
|
18
22
|
var(--dx-g-global-header-padding-horizontal);
|
|
@@ -26,6 +26,28 @@
|
|
|
26
26
|
--height: var(--dx-g-spacing-mlg);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
.dx-badge.status-success,
|
|
30
|
+
.dx-badge.status-warning,
|
|
31
|
+
.dx-badge.status-error {
|
|
32
|
+
/* No spacing token maps to 10px; status badges need this exact padding. */
|
|
33
|
+
--horizontal-spacing: 10px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.dx-badge.status-success {
|
|
37
|
+
--color: var(--sds-g-gray-1);
|
|
38
|
+
--background: var(--dx-g-green-vibrant-50);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.dx-badge.status-warning {
|
|
42
|
+
--color: var(--dx-g-gray-15);
|
|
43
|
+
--background: var(--dx-g-yellow-vibrant-80);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.dx-badge.status-error {
|
|
47
|
+
--color: var(--sds-g-gray-1);
|
|
48
|
+
--background: var(--dx-g-red-vibrant-40);
|
|
49
|
+
}
|
|
50
|
+
|
|
29
51
|
svg,
|
|
30
52
|
dx-icon {
|
|
31
53
|
margin-right: var(--dx-g-spacing-sm);
|