@reliability-design/web 1.0.6 → 1.1.1
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/dist/components/rel-accordion/rel-accordion.ts +4 -0
- package/dist/components/rel-alert/rel-alert.ts +83 -13
- package/dist/components/rel-avatar/rel-avatar.ts +3 -2
- package/dist/components/rel-badge/rel-badge.ts +6 -2
- package/dist/components/rel-button/rel-button.ts +64 -8
- package/dist/components/rel-calendar/rel-calendar.ts +3 -3
- package/dist/components/rel-carousel/rel-carousel.ts +10 -11
- package/dist/components/rel-checkbox/rel-checkbox.ts +33 -2
- package/dist/components/rel-chip/rel-chip.ts +63 -5
- package/dist/components/rel-divider/rel-divider.ts +9 -0
- package/dist/components/rel-drawer/rel-drawer-footer.ts +29 -0
- package/dist/components/rel-drawer/rel-drawer.ts +17 -26
- package/dist/components/rel-menu/rel-menu.ts +53 -21
- package/dist/components/rel-menubar/rel-menubar.ts +52 -23
- package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +240 -143
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +227 -331
- package/dist/components/rel-navbar/rel-navbar.ts +13 -0
- package/dist/components/rel-pagination/rel-pagination.ts +11 -6
- package/dist/components/rel-popover/rel-popover.ts +16 -13
- package/dist/components/rel-radio/rel-radio.ts +18 -1
- package/dist/components/rel-resizable/rel-resizable-handle.ts +4 -8
- package/dist/components/rel-select/rel-select.ts +14 -0
- package/dist/components/rel-sheet/rel-sheet.ts +7 -6
- package/dist/components/rel-sidebar/rel-sidebar.ts +13 -5
- package/dist/components/rel-slider/rel-slider.ts +25 -2
- package/dist/components/rel-snackbar/rel-snackbar.ts +46 -16
- package/dist/components/rel-stepper/rel-stepper.ts +32 -21
- package/dist/components/rel-switch/rel-switch.ts +19 -6
- package/dist/components/rel-tab/rel-tab.ts +49 -15
- package/dist/components/rel-table/rel-table-body.ts +5 -0
- package/dist/components/rel-table/rel-table-caption.ts +6 -1
- package/dist/components/rel-table/rel-table-cell.ts +42 -49
- package/dist/components/rel-table/rel-table-footer.ts +5 -0
- package/dist/components/rel-table/rel-table-head.ts +13 -1
- package/dist/components/rel-table/rel-table-header.ts +5 -0
- package/dist/components/rel-table/rel-table-row.ts +7 -3
- package/dist/components/rel-table/rel-table.ts +8 -2
- package/dist/components/rel-textfield/rel-textfield.ts +20 -20
- package/dist/components/rel-toast/rel-toast.ts +31 -10
- package/dist/components/rel-tooltip/rel-tooltip.ts +123 -80
- package/dist/components/rel-topbar/rel-topbar-actions.ts +23 -0
- package/dist/components/rel-topbar/rel-topbar-brand.ts +46 -0
- package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +33 -0
- package/dist/components/rel-topbar/rel-topbar-dropdown.ts +28 -0
- package/dist/components/rel-topbar/rel-topbar-item.ts +315 -0
- package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +23 -0
- package/dist/components/rel-topbar/rel-topbar-profile.ts +248 -0
- package/dist/components/rel-topbar/rel-topbar-utilities.ts +23 -0
- package/dist/components/rel-topbar/rel-topbar.ts +329 -0
- package/dist/index.d.mts +278 -289
- package/dist/index.d.ts +278 -289
- package/dist/index.js +4498 -5727
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4535 -5756
- package/dist/index.mjs.map +1 -1
- package/dist/styles/vars.css +346 -294
- package/dist/{vars-ITL2K3KO.css → vars-TBB4BMSD.css} +346 -294
- package/package.json +2 -1
- package/dist/components/rel-quote-page/README.md +0 -166
- package/dist/components/rel-quote-page/rel-quote-page.ts +0 -816
|
@@ -1,80 +1,123 @@
|
|
|
1
|
-
import { LitElement, html, css } from "lit";
|
|
2
|
-
import { customElement, property } from "lit/decorators.js";
|
|
3
|
-
|
|
4
|
-
export type TooltipPosition = "above" | "below";
|
|
5
|
-
|
|
6
|
-
@customElement("rel-tooltip")
|
|
7
|
-
export class RelTooltip extends LitElement {
|
|
8
|
-
@property({ type: String }) text: string = "";
|
|
9
|
-
@property({ type: String }) position: TooltipPosition = "above";
|
|
10
|
-
@property({ type: Boolean }) showTail: boolean =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
export type TooltipPosition = "above" | "below";
|
|
5
|
+
|
|
6
|
+
@customElement("rel-tooltip")
|
|
7
|
+
export class RelTooltip extends LitElement {
|
|
8
|
+
@property({ type: String }) text: string = "";
|
|
9
|
+
@property({ type: String }) position: TooltipPosition = "above";
|
|
10
|
+
@property({ type: Boolean, attribute: "show-tail" }) showTail: boolean =
|
|
11
|
+
false;
|
|
12
|
+
|
|
13
|
+
// Stable id so aria-describedby can reference the bubble across renders.
|
|
14
|
+
private uniqueId = Math.random().toString(36).slice(2);
|
|
15
|
+
|
|
16
|
+
static styles = css`
|
|
17
|
+
:host {
|
|
18
|
+
display: inline-block;
|
|
19
|
+
position: relative;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Tooltip container */
|
|
23
|
+
.tooltip-bubble {
|
|
24
|
+
position: absolute;
|
|
25
|
+
left: 50%;
|
|
26
|
+
transform: translateX(-50%);
|
|
27
|
+
background-color: #1a1a1a;
|
|
28
|
+
color: #ffffff;
|
|
29
|
+
padding: var(--rel-spacing-space-xs, 8px)
|
|
30
|
+
var(--rel-spacing-space-sm, 12px);
|
|
31
|
+
border-radius: var(--rel-borders-radius-border-radius-200, 8px);
|
|
32
|
+
font-size: var(--rel-fontSizes-sm, 12px);
|
|
33
|
+
font-family: var(--rel-fontFamilies-primary, sans-serif);
|
|
34
|
+
white-space: nowrap;
|
|
35
|
+
z-index: 1000;
|
|
36
|
+
opacity: 0;
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
transition: opacity 0.2s ease;
|
|
39
|
+
overflow: visible;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
:host(:hover) .tooltip-bubble,
|
|
43
|
+
:host(:focus-within) .tooltip-bubble {
|
|
44
|
+
opacity: 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Positioning */
|
|
48
|
+
.tooltip-bubble.above {
|
|
49
|
+
bottom: calc(100% + 8px);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.tooltip-bubble.below {
|
|
53
|
+
top: calc(100% + 8px);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Tail/Arrow */
|
|
57
|
+
.tooltip-tail {
|
|
58
|
+
position: absolute;
|
|
59
|
+
left: 50%;
|
|
60
|
+
transform: translateX(-50%);
|
|
61
|
+
width: 0;
|
|
62
|
+
height: 0;
|
|
63
|
+
border-left: 6px solid transparent;
|
|
64
|
+
border-right: 6px solid transparent;
|
|
65
|
+
display: block;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.tooltip-bubble.above .tooltip-tail {
|
|
69
|
+
bottom: -6px;
|
|
70
|
+
border-top: 6px solid #1a1a1a;
|
|
71
|
+
border-bottom: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.tooltip-bubble.below .tooltip-tail {
|
|
75
|
+
top: -6px;
|
|
76
|
+
border-bottom: 6px solid #1a1a1a;
|
|
77
|
+
border-top: none;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
:host-context([data-theme="dark"]) .tooltip-bubble,
|
|
81
|
+
:host-context(.dark) .tooltip-bubble {
|
|
82
|
+
background-color: #ffffff;
|
|
83
|
+
color: #000000;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
:host-context([data-theme="dark"]) .tooltip-bubble.above .tooltip-tail,
|
|
87
|
+
:host-context(.dark) .tooltip-bubble.above .tooltip-tail {
|
|
88
|
+
border-top-color: #ffffff;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
:host-context([data-theme="dark"]) .tooltip-bubble.below .tooltip-tail,
|
|
92
|
+
:host-context(.dark) .tooltip-bubble.below .tooltip-tail {
|
|
93
|
+
border-bottom-color: #ffffff;
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
firstUpdated() {
|
|
98
|
+
this.linkTrigger();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private linkTrigger() {
|
|
102
|
+
const slot = this.shadowRoot?.querySelector("slot");
|
|
103
|
+
if (!slot) return;
|
|
104
|
+
const trigger = slot.assignedElements()[0];
|
|
105
|
+
if (trigger) {
|
|
106
|
+
trigger.setAttribute("aria-describedby", `tooltip-${this.uniqueId}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
render() {
|
|
111
|
+
return html`
|
|
112
|
+
<slot @slotchange="${this.linkTrigger}"></slot>
|
|
113
|
+
<div
|
|
114
|
+
class="tooltip-bubble ${this.position}"
|
|
115
|
+
id="tooltip-${this.uniqueId}"
|
|
116
|
+
role="tooltip"
|
|
117
|
+
>
|
|
118
|
+
${this.text}
|
|
119
|
+
${this.showTail ? html`<span class="tooltip-tail"></span>` : ""}
|
|
120
|
+
</div>
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-topbar-actions")
|
|
5
|
+
export class RelTopbarActions extends LitElement {
|
|
6
|
+
static styles = css`
|
|
7
|
+
.actions {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: var(--rel-spacing-space-xs, 8px);
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
render() {
|
|
15
|
+
return html`<div class="actions"><slot></slot></div>`;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare global {
|
|
20
|
+
interface HTMLElementTagNameMap {
|
|
21
|
+
"rel-topbar-actions": RelTopbarActions;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-topbar-brand")
|
|
5
|
+
export class RelTopbarBrand extends LitElement {
|
|
6
|
+
@property({ type: String }) src: string = "";
|
|
7
|
+
@property({ type: String }) href: string = "/";
|
|
8
|
+
@property({ type: String }) label: string = "";
|
|
9
|
+
|
|
10
|
+
static styles = css`
|
|
11
|
+
:host {
|
|
12
|
+
display: inline-flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
a {
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
gap: var(--rel-spacing-space-xs);
|
|
20
|
+
text-decoration: none;
|
|
21
|
+
color: var(--rel-color-text-primary);
|
|
22
|
+
font-weight: var(--rel-fontWeights-semibold);
|
|
23
|
+
font-size: var(--rel-fontSizes-md);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
img {
|
|
27
|
+
display: block;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
render() {
|
|
32
|
+
return html`
|
|
33
|
+
<a href="${this.href}" aria-label="${this.label || "Home"}">
|
|
34
|
+
${this.src
|
|
35
|
+
? html`<img src="${this.src}" alt="${this.label}" />`
|
|
36
|
+
: html`<slot>${this.label}</slot>`}
|
|
37
|
+
</a>
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
declare global {
|
|
43
|
+
interface HTMLElementTagNameMap {
|
|
44
|
+
"rel-topbar-brand": RelTopbarBrand;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-topbar-dropdown-item")
|
|
5
|
+
export class RelTopbarDropdownItem extends LitElement {
|
|
6
|
+
@property({ type: String }) label: string = "";
|
|
7
|
+
@property({ type: String }) href: string = "#";
|
|
8
|
+
|
|
9
|
+
static styles = css`
|
|
10
|
+
.dropdown-item {
|
|
11
|
+
display: block;
|
|
12
|
+
padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-md);
|
|
13
|
+
color: var(--rel-color-text-primary);
|
|
14
|
+
text-decoration: none;
|
|
15
|
+
font-size: var(--rel-fontSizes-md);
|
|
16
|
+
font-family: var(--rel-fontFamilies-primary, sans-serif);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.dropdown-item:hover {
|
|
20
|
+
background: var(--rel-color-bg-hover);
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
render() {
|
|
25
|
+
return html`<a href="${this.href}" class="dropdown-item">${this.label}</a>`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
declare global {
|
|
30
|
+
interface HTMLElementTagNameMap {
|
|
31
|
+
"rel-topbar-dropdown-item": RelTopbarDropdownItem;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-topbar-dropdown")
|
|
5
|
+
export class RelTopbarDropdown extends LitElement {
|
|
6
|
+
static styles = css`
|
|
7
|
+
.dropdown {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
min-width: 160px;
|
|
11
|
+
background: var(--rel-color-bg-primary);
|
|
12
|
+
border: 1px solid var(--rel-color-border-default);
|
|
13
|
+
border-radius: var(--rel-borders-radius-border-radius-200);
|
|
14
|
+
box-shadow: var(--rel-shadow-200);
|
|
15
|
+
padding: var(--rel-spacing-space-2xs) 0;
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
render() {
|
|
20
|
+
return html`<div class="dropdown"><slot></slot></div>`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare global {
|
|
25
|
+
interface HTMLElementTagNameMap {
|
|
26
|
+
"rel-topbar-dropdown": RelTopbarDropdown;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
+
import "./rel-topbar-dropdown";
|
|
4
|
+
import "./rel-topbar-dropdown-item";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `rel-topbar-item` — A navigation item within rel-topbar.
|
|
8
|
+
*
|
|
9
|
+
* Renders as an `<a>` when `href` is supplied, otherwise a `<button>`.
|
|
10
|
+
*
|
|
11
|
+
* Slots:
|
|
12
|
+
* (default) — Label text / icon (overrides `label` property)
|
|
13
|
+
* prefix — Icon/content before the label
|
|
14
|
+
* suffix — Icon/content after the label (e.g. badge)
|
|
15
|
+
* dropdown — Expandable panel content shown below the item
|
|
16
|
+
*
|
|
17
|
+
* Events:
|
|
18
|
+
* rel-select — fired when a non-disabled, non-expandable item is activated
|
|
19
|
+
* rel-toggle — fired when an expandable item is opened/closed { open: boolean }
|
|
20
|
+
*/
|
|
21
|
+
@customElement("rel-topbar-item")
|
|
22
|
+
export class RelTopbarItem extends LitElement {
|
|
23
|
+
/** Accessible label — also shown as text when no default slot content */
|
|
24
|
+
@property({ type: String }) label?: string;
|
|
25
|
+
|
|
26
|
+
/** Navigation href — renders an anchor element when set */
|
|
27
|
+
@property({ type: String }) href?: string;
|
|
28
|
+
|
|
29
|
+
/** Marks this item as the current/active page */
|
|
30
|
+
@property({ type: Boolean, reflect: true }) active = false;
|
|
31
|
+
|
|
32
|
+
/** Prevents interaction */
|
|
33
|
+
@property({ type: Boolean, reflect: true }) disabled = false;
|
|
34
|
+
|
|
35
|
+
/** Shows a chevron and enables dropdown panel */
|
|
36
|
+
@property({ type: Boolean, reflect: true }) expandable = false;
|
|
37
|
+
|
|
38
|
+
/** Controlled open state of the dropdown panel */
|
|
39
|
+
@property({ type: Boolean, reflect: true }) open = false;
|
|
40
|
+
|
|
41
|
+
/** Optional string value forwarded in rel-select / rel-toggle events */
|
|
42
|
+
@property({ type: String }) value?: string;
|
|
43
|
+
|
|
44
|
+
/** @internal tracks whether dropdown slot has content */
|
|
45
|
+
@state() private _hasDropdown = false;
|
|
46
|
+
|
|
47
|
+
static styles = css`
|
|
48
|
+
/* ─── Component tokens ──────────────────────────────────────────── */
|
|
49
|
+
:host {
|
|
50
|
+
--_item-color: var(--rel-topbar-item-color, var(--rel-text-primary, #333));
|
|
51
|
+
--_item-active-color: var(--rel-topbar-item-active-color, var(--rel-colors-green-tertiary, #00a86b));
|
|
52
|
+
--_item-active-indicator: var(--rel-topbar-item-active-indicator, var(--rel-colors-green-primary, #00f49c));
|
|
53
|
+
--_item-hover-bg: var(--rel-topbar-item-hover-bg, var(--rel-surface-secondary, #fafafa));
|
|
54
|
+
|
|
55
|
+
display: inline-block;
|
|
56
|
+
position: relative;
|
|
57
|
+
font-family: var(--rel-fontFamilies-primary, Poppins, sans-serif);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* ─── Trigger (shared for <a> and <button>) ─────────────────────── */
|
|
61
|
+
.trigger {
|
|
62
|
+
display: inline-flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: var(--rel-spacing-space-2xs, 4px);
|
|
65
|
+
padding: var(--rel-spacing-space-xs, 8px) var(--rel-spacing-space-sm, 12px);
|
|
66
|
+
border: none;
|
|
67
|
+
background: transparent;
|
|
68
|
+
border-radius: var(--rel-borders-radius-border-radius-200, 8px);
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
color: var(--_item-color);
|
|
71
|
+
font-family: inherit;
|
|
72
|
+
font-size: var(--rel-fontSizes-md, 14px);
|
|
73
|
+
font-weight: var(--rel-fontWeights-medium, 500);
|
|
74
|
+
line-height: var(--rel-lineHeights-md, 20px);
|
|
75
|
+
text-decoration: none;
|
|
76
|
+
white-space: nowrap;
|
|
77
|
+
position: relative;
|
|
78
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.trigger::after {
|
|
83
|
+
content: "";
|
|
84
|
+
position: absolute;
|
|
85
|
+
bottom: -2px;
|
|
86
|
+
left: var(--rel-spacing-space-sm, 12px);
|
|
87
|
+
right: var(--rel-spacing-space-sm, 12px);
|
|
88
|
+
height: 2px;
|
|
89
|
+
border-radius: 1px;
|
|
90
|
+
background: transparent;
|
|
91
|
+
transition: background 0.15s ease;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Active state */
|
|
95
|
+
:host([active]) .trigger {
|
|
96
|
+
color: var(--_item-active-color);
|
|
97
|
+
font-weight: var(--rel-fontWeights-semibold, 600);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
:host([active]) .trigger::after {
|
|
101
|
+
background: var(--_item-active-indicator);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Hover */
|
|
105
|
+
.trigger:hover:not(:disabled) {
|
|
106
|
+
background: var(--_item-hover-bg);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Focus */
|
|
110
|
+
.trigger:focus-visible {
|
|
111
|
+
outline: 2px solid var(--_item-active-indicator);
|
|
112
|
+
outline-offset: 2px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Disabled */
|
|
116
|
+
:host([disabled]) .trigger {
|
|
117
|
+
opacity: 0.45;
|
|
118
|
+
cursor: not-allowed;
|
|
119
|
+
pointer-events: none;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Open state (expandable) */
|
|
123
|
+
:host([open]) .trigger {
|
|
124
|
+
background: var(--_item-hover-bg);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* ─── Prefix / suffix slots ─────────────────────────────────────── */
|
|
128
|
+
.prefix,
|
|
129
|
+
.suffix {
|
|
130
|
+
display: contents;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ─── Chevron icon ──────────────────────────────────────────────── */
|
|
134
|
+
.chevron {
|
|
135
|
+
display: inline-flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
flex-shrink: 0;
|
|
138
|
+
transition: transform 0.2s ease;
|
|
139
|
+
color: inherit;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
:host([open]) .chevron {
|
|
143
|
+
transform: rotate(180deg);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* ─── Dropdown panel ────────────────────────────────────────────── */
|
|
147
|
+
.dropdown {
|
|
148
|
+
position: absolute;
|
|
149
|
+
top: calc(100% + 6px);
|
|
150
|
+
left: 0;
|
|
151
|
+
min-width: 200px;
|
|
152
|
+
background: var(--rel-colors-white, #fff);
|
|
153
|
+
border: var(--rel-borders-width-stroke-100, 1px) solid var(--rel-border-default, #ddd);
|
|
154
|
+
border-radius: var(--rel-borders-radius-border-radius-200, 8px);
|
|
155
|
+
box-shadow: var(--rel-shadows-shadow-300);
|
|
156
|
+
padding: var(--rel-spacing-space-2xs, 4px);
|
|
157
|
+
z-index: 200;
|
|
158
|
+
display: none;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
:host([open]) .dropdown {
|
|
162
|
+
display: block;
|
|
163
|
+
}
|
|
164
|
+
`;
|
|
165
|
+
|
|
166
|
+
connectedCallback() {
|
|
167
|
+
super.connectedCallback();
|
|
168
|
+
document.addEventListener("click", this._handleOutsideClick);
|
|
169
|
+
document.addEventListener("keydown", this._handleDocKeydown);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
disconnectedCallback() {
|
|
173
|
+
super.disconnectedCallback();
|
|
174
|
+
document.removeEventListener("click", this._handleOutsideClick);
|
|
175
|
+
document.removeEventListener("keydown", this._handleDocKeydown);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private _handleOutsideClick = (e: MouseEvent) => {
|
|
179
|
+
if (!this.open) return;
|
|
180
|
+
const path = e.composedPath() as Node[];
|
|
181
|
+
if (!path.includes(this)) {
|
|
182
|
+
this._setOpen(false);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
private _handleDocKeydown = (e: KeyboardEvent) => {
|
|
187
|
+
if (e.key === "Escape" && this.open) {
|
|
188
|
+
this._setOpen(false);
|
|
189
|
+
(this.shadowRoot?.querySelector(".trigger") as HTMLElement)?.focus();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
private _setOpen(value: boolean) {
|
|
194
|
+
this.open = value;
|
|
195
|
+
this.dispatchEvent(
|
|
196
|
+
new CustomEvent("rel-toggle", {
|
|
197
|
+
detail: { open: this.open, value: this.value },
|
|
198
|
+
bubbles: true,
|
|
199
|
+
composed: true,
|
|
200
|
+
})
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private _handleTriggerClick(e: MouseEvent) {
|
|
205
|
+
if (this.disabled) {
|
|
206
|
+
e.preventDefault();
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (this.expandable || this._hasDropdown) {
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
this._setOpen(!this.open);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!this.href) {
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this.dispatchEvent(
|
|
221
|
+
new CustomEvent("rel-select", {
|
|
222
|
+
detail: { value: this.value, label: this.label },
|
|
223
|
+
bubbles: true,
|
|
224
|
+
composed: true,
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// Bubble up to topbar
|
|
229
|
+
this.dispatchEvent(
|
|
230
|
+
new CustomEvent("rel-nav-select", {
|
|
231
|
+
detail: { value: this.value, label: this.label },
|
|
232
|
+
bubbles: true,
|
|
233
|
+
composed: true,
|
|
234
|
+
})
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private _routeSlots(e: Event) {
|
|
239
|
+
const slot = e.target as HTMLSlotElement;
|
|
240
|
+
for (const el of slot.assignedElements({ flatten: true })) {
|
|
241
|
+
const targetSlot = (el as HTMLElement).tagName.toLowerCase() === "rel-topbar-dropdown" ? "dropdown" : "";
|
|
242
|
+
if ((el as HTMLElement).slot !== targetSlot) {
|
|
243
|
+
(el as HTMLElement).slot = targetSlot;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private _handleDropdownSlotChange(e: Event) {
|
|
249
|
+
const slot = e.target as HTMLSlotElement;
|
|
250
|
+
const nodes = slot.assignedElements();
|
|
251
|
+
this._hasDropdown = nodes.length > 0;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
render() {
|
|
255
|
+
const isLink = !!this.href && !this.disabled;
|
|
256
|
+
const showChevron = this.expandable || this._hasDropdown;
|
|
257
|
+
const ariaCurrent = this.active ? "page" : undefined;
|
|
258
|
+
const ariaExpanded = showChevron ? (this.open ? "true" : "false") : undefined;
|
|
259
|
+
const ariaHasPopup = showChevron ? "true" : undefined;
|
|
260
|
+
|
|
261
|
+
const triggerContent = html`
|
|
262
|
+
<span class="prefix"><slot name="prefix"></slot></span>
|
|
263
|
+
<slot>${this.label ?? ""}</slot>
|
|
264
|
+
<span class="suffix"><slot name="suffix"></slot></span>
|
|
265
|
+
${showChevron
|
|
266
|
+
? html`<span class="chevron" aria-hidden="true">${_iconChevron()}</span>`
|
|
267
|
+
: null}
|
|
268
|
+
`;
|
|
269
|
+
|
|
270
|
+
const trigger = isLink
|
|
271
|
+
? html`
|
|
272
|
+
<a
|
|
273
|
+
class="trigger"
|
|
274
|
+
href=${this.href!}
|
|
275
|
+
aria-current=${ariaCurrent as string}
|
|
276
|
+
aria-expanded=${ariaExpanded as string}
|
|
277
|
+
aria-haspopup=${ariaHasPopup as string}
|
|
278
|
+
@click=${this._handleTriggerClick}
|
|
279
|
+
>
|
|
280
|
+
${triggerContent}
|
|
281
|
+
</a>
|
|
282
|
+
`
|
|
283
|
+
: html`
|
|
284
|
+
<button
|
|
285
|
+
class="trigger"
|
|
286
|
+
type="button"
|
|
287
|
+
?disabled=${this.disabled}
|
|
288
|
+
aria-current=${ariaCurrent as string}
|
|
289
|
+
aria-expanded=${ariaExpanded as string}
|
|
290
|
+
aria-haspopup=${ariaHasPopup as string}
|
|
291
|
+
@click=${this._handleTriggerClick}
|
|
292
|
+
>
|
|
293
|
+
${triggerContent}
|
|
294
|
+
</button>
|
|
295
|
+
`;
|
|
296
|
+
|
|
297
|
+
return html`
|
|
298
|
+
<!-- router: auto-assigns slot="dropdown" for rel-topbar-dropdown children -->
|
|
299
|
+
<slot style="display:none" @slotchange=${this._routeSlots}></slot>
|
|
300
|
+
|
|
301
|
+
${trigger}
|
|
302
|
+
<div class="dropdown" role="region">
|
|
303
|
+
<slot name="dropdown" @slotchange=${this._handleDropdownSlotChange}></slot>
|
|
304
|
+
</div>
|
|
305
|
+
`;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function _iconChevron() {
|
|
310
|
+
return html`
|
|
311
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true" focusable="false">
|
|
312
|
+
<path d="M3 5L7 9L11 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
313
|
+
</svg>
|
|
314
|
+
`;
|
|
315
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
|
|
4
|
+
@customElement("rel-topbar-mobile-panel")
|
|
5
|
+
export class RelTopbarMobilePanel extends LitElement {
|
|
6
|
+
static styles = css`
|
|
7
|
+
.mobile-panel {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
width: 100%;
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
render() {
|
|
15
|
+
return html`<div class="mobile-panel"><slot></slot></div>`;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare global {
|
|
20
|
+
interface HTMLElementTagNameMap {
|
|
21
|
+
"rel-topbar-mobile-panel": RelTopbarMobilePanel;
|
|
22
|
+
}
|
|
23
|
+
}
|