@fluid-topics/ft-tabs 0.2.22 → 0.3.2

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/build/ft-tab.d.ts CHANGED
@@ -1,22 +1,22 @@
1
1
  import { ElementDefinitionsMap, FtLitElement } from "@fluid-topics/ft-wc-utils";
2
2
  import { PropertyValues } from "lit";
3
- export declare enum FtTabPosition {
4
- before = "before",
5
- current = "current",
6
- after = "after"
7
- }
3
+ import { FtIconVariants } from "@fluid-topics/ft-icon";
8
4
  export interface FtTabProperties {
9
- label: string;
10
- icon: string;
11
- disabled: boolean;
5
+ label?: string;
6
+ icon?: string;
7
+ disabled?: boolean;
8
+ iconVariant?: FtIconVariants;
12
9
  }
13
10
  export declare class FtTab extends FtLitElement implements FtTabProperties {
14
11
  static elementDefinitions: ElementDefinitionsMap;
15
- static get styles(): import("lit").CSSResult;
16
12
  label: string;
17
13
  icon: string;
18
- visible: boolean;
14
+ iconVariant: FtIconVariants;
15
+ active: boolean;
19
16
  disabled: boolean;
17
+ private readonly uniqueId;
18
+ id: string;
19
+ ariaLabelledBy?: string;
20
20
  protected render(): unknown;
21
21
  protected updated(props: PropertyValues): void;
22
22
  }
package/build/ft-tab.js CHANGED
@@ -5,36 +5,24 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { FtLitElement } from "@fluid-topics/ft-wc-utils";
8
- import { css, html, unsafeCSS } from "lit";
8
+ import { html, unsafeCSS } from "lit";
9
9
  import { property } from "lit/decorators.js";
10
- export var FtTabPosition;
11
- (function (FtTabPosition) {
12
- FtTabPosition["before"] = "before";
13
- FtTabPosition["current"] = "current";
14
- FtTabPosition["after"] = "after";
15
- })(FtTabPosition || (FtTabPosition = {}));
10
+ import { FtIconVariants } from "@fluid-topics/ft-icon";
16
11
  export class FtTab extends FtLitElement {
17
12
  constructor() {
18
13
  super(...arguments);
19
14
  this.label = "";
20
15
  this.icon = "";
21
- this.visible = false;
16
+ this.iconVariant = FtIconVariants.material;
17
+ this.active = false;
22
18
  this.disabled = false;
23
- }
24
- static get styles() {
25
- // language=CSS
26
- return css `
27
- :host {
28
- width: 100%;
29
- position: absolute;
30
- }
31
- `;
19
+ this.uniqueId = ("" + Math.floor(Math.random() * 100000)).padStart(5, "0");
32
20
  }
33
21
  render() {
34
22
  return html `
35
23
  <style>
36
24
  :host {
37
- display: ${unsafeCSS(this.visible ? "block" : "none")};
25
+ display: ${unsafeCSS(this.active ? "block" : "none")};
38
26
  }
39
27
  </style>
40
28
  <slot></slot>
@@ -42,6 +30,10 @@ export class FtTab extends FtLitElement {
42
30
  }
43
31
  updated(props) {
44
32
  super.updated(props);
33
+ if (!this.id) {
34
+ this.id = "tab-" + this.uniqueId;
35
+ }
36
+ this.ariaLabelledBy = this.id + "-control";
45
37
  this.dispatchEvent(new Event("updated"));
46
38
  }
47
39
  }
@@ -52,10 +44,19 @@ __decorate([
52
44
  __decorate([
53
45
  property({ type: String })
54
46
  ], FtTab.prototype, "icon", void 0);
47
+ __decorate([
48
+ property()
49
+ ], FtTab.prototype, "iconVariant", void 0);
55
50
  __decorate([
56
51
  property({ type: Boolean })
57
- ], FtTab.prototype, "visible", void 0);
52
+ ], FtTab.prototype, "active", void 0);
58
53
  __decorate([
59
54
  property({ type: Boolean })
60
55
  ], FtTab.prototype, "disabled", void 0);
56
+ __decorate([
57
+ property({ type: String, reflect: true })
58
+ ], FtTab.prototype, "id", void 0);
59
+ __decorate([
60
+ property({ type: String, reflect: true, attribute: "aria-labelledby" })
61
+ ], FtTab.prototype, "ariaLabelledBy", void 0);
61
62
  //# sourceMappingURL=ft-tab.js.map
@@ -1,15 +1,21 @@
1
1
  import { PropertyValues } from "lit";
2
- import { ElementDefinitionsMap, FtCssVariable, FtLitElement } from "@fluid-topics/ft-wc-utils";
2
+ import { ElementDefinitionsMap, FtLitElement } from "@fluid-topics/ft-wc-utils";
3
+ export declare enum FtTabsAlignment {
4
+ left = "left",
5
+ right = "right",
6
+ justify = "justify"
7
+ }
3
8
  export interface FtTabsProperties {
4
9
  dense: boolean;
5
10
  contentBefore: boolean;
6
11
  activeIndex: number;
12
+ alignTabs: FtTabsAlignment;
7
13
  }
8
14
  export declare const FtTabsCssVariables: {
9
- colorSurface: FtCssVariable;
10
- colorPrimary: FtCssVariable;
11
- colorOnSurfaceMedium: FtCssVariable;
12
- colorOnSurfaceDisabled: FtCssVariable;
15
+ colorSurface: import("@fluid-topics/ft-wc-utils").FtCssVariable;
16
+ colorPrimary: import("@fluid-topics/ft-wc-utils").FtCssVariable;
17
+ colorOnSurfaceMedium: import("@fluid-topics/ft-wc-utils").FtCssVariable;
18
+ colorOnSurfaceDisabled: import("@fluid-topics/ft-wc-utils").FtCssVariable;
13
19
  };
14
20
  export declare class IndexChangeEvent extends CustomEvent<number> {
15
21
  constructor(index: number);
@@ -19,18 +25,20 @@ export declare class FtTabs extends FtLitElement implements FtTabsProperties {
19
25
  static styles: import("lit").CSSResult;
20
26
  dense: boolean;
21
27
  contentBefore: boolean;
22
- private tabs;
28
+ alignTabs: FtTabsAlignment;
29
+ private ftTabs;
30
+ private tabsContainer?;
31
+ private activeTab?;
32
+ private activeTabIndicator?;
23
33
  activeIndex: number;
24
- private resizable?;
25
- private get currentTab();
26
- private resizeObserver;
27
34
  protected render(): import("lit-html").TemplateResult<1>;
28
- private onTabChange;
29
35
  private updateDebouncer;
36
+ private scheduleRequestUpdate;
30
37
  private onContentChange;
31
38
  protected updated(props: PropertyValues): void;
32
- private placeTabs;
33
- private resizeDebouncer;
34
- private resize;
39
+ private resizeObserver;
40
+ protected contentAvailableCallback(props: PropertyValues): void;
41
+ private placeIndicator;
42
+ private updateTabs;
35
43
  }
36
44
  //# sourceMappingURL=ft-tabs.d.ts.map
package/build/ft-tabs.js CHANGED
@@ -8,15 +8,22 @@ import { css, html } from "lit";
8
8
  import { property, query, state } from "lit/decorators.js";
9
9
  import { repeat } from "lit/directives/repeat.js";
10
10
  import { classMap } from "lit/directives/class-map.js";
11
- import { Debouncer, designSystemVariables, FtCssVariable, FtLitElement } from "@fluid-topics/ft-wc-utils";
11
+ import { Debouncer, designSystemVariables, FtCssVariableFactory, FtLitElement } from "@fluid-topics/ft-wc-utils";
12
12
  import { FtTab } from "./ft-tab";
13
- import { Tab } from "@material/mwc-tab";
14
- import { TabBar } from "@material/mwc-tab-bar";
13
+ import { FtRipple } from "@fluid-topics/ft-ripple";
14
+ import { FtTypography, FtTypographyVariants } from "@fluid-topics/ft-typography";
15
+ import { FtIcon } from "@fluid-topics/ft-icon";
16
+ export var FtTabsAlignment;
17
+ (function (FtTabsAlignment) {
18
+ FtTabsAlignment["left"] = "left";
19
+ FtTabsAlignment["right"] = "right";
20
+ FtTabsAlignment["justify"] = "justify";
21
+ })(FtTabsAlignment || (FtTabsAlignment = {}));
15
22
  export const FtTabsCssVariables = {
16
- colorSurface: FtCssVariable.external(designSystemVariables.colorSurface, "Design system"),
17
- colorPrimary: FtCssVariable.external(designSystemVariables.colorPrimary, "Design system"),
18
- colorOnSurfaceMedium: FtCssVariable.external(designSystemVariables.colorOnSurfaceMedium, "Design system"),
19
- colorOnSurfaceDisabled: FtCssVariable.external(designSystemVariables.colorOnSurfaceDisabled, "Design system"),
23
+ colorSurface: FtCssVariableFactory.external(designSystemVariables.colorSurface, "Design system"),
24
+ colorPrimary: FtCssVariableFactory.external(designSystemVariables.colorPrimary, "Design system"),
25
+ colorOnSurfaceMedium: FtCssVariableFactory.external(designSystemVariables.colorOnSurfaceMedium, "Design system"),
26
+ colorOnSurfaceDisabled: FtCssVariableFactory.external(designSystemVariables.colorOnSurfaceDisabled, "Design system"),
20
27
  };
21
28
  export class IndexChangeEvent extends CustomEvent {
22
29
  constructor(index) {
@@ -28,79 +35,106 @@ export class FtTabs extends FtLitElement {
28
35
  super(...arguments);
29
36
  this.dense = false;
30
37
  this.contentBefore = false;
31
- this.tabs = [];
38
+ this.alignTabs = FtTabsAlignment.justify;
39
+ this.ftTabs = [];
32
40
  this.activeIndex = 0;
33
- this.resizeObserver = new ResizeObserver(() => this.resize());
34
41
  this.updateDebouncer = new Debouncer(20);
35
- this.resizeDebouncer = new Debouncer(50);
36
- }
37
- get currentTab() {
38
- return this.tabs[this.activeIndex];
42
+ this.scheduleRequestUpdate = () => this.updateDebouncer.run(() => this.requestUpdate());
43
+ this.resizeObserver = new ResizeObserver(() => this.placeIndicator());
39
44
  }
40
45
  render() {
41
46
  const classes = {
42
47
  "ft-tabs": true,
43
48
  "ft-tabs--reverse": this.contentBefore,
49
+ "ft-tabs--dense": this.dense,
44
50
  };
45
51
  return html `
46
- <div class="${classMap(classes)}">
47
- <mwc-tab-bar @MDCTabBar:activated=${this.onTabChange} .activeIndex=${this.activeIndex}>
48
- ${repeat(this.tabs, tab => html `
49
- <mwc-tab label="${this.dense && tab.icon ? "" : tab.label}"
50
- title="${this.dense && tab.icon ? tab.label : ""}"
51
- aria-label="${tab.label}"
52
- icon="${tab.icon}"
53
- ?stacked=${!this.dense}
54
- ?disabled=${tab.disabled}></mwc-tab>
52
+ <div class="${classMap(classes)}" part="container">
53
+ <div class="ft-tabs--align-${this.alignTabs}" role="tablist" part="tablist">
54
+ ${repeat(this.ftTabs, (tab, index) => html `
55
+ <button class="${classMap({
56
+ "ft-tabs--tab-with-icon": !!tab.icon,
57
+ "ft-tabs--tab-with-label": !!tab.label,
58
+ })}"
59
+ title="${tab.label}"
60
+ ?disabled=${tab.disabled}
61
+ @click=${() => this.activeIndex = index}
62
+ role="tab"
63
+ part="tab"
64
+ id="${tab.ariaLabelledBy}"
65
+ aria-label="${tab.label}"
66
+ aria-selected="${tab.active ? "true" : "false"}"
67
+ tabindex="${tab.disabled ? "-1" : "0"}"
68
+ aria-controls="${tab.id}"
69
+ >
70
+ <ft-ripple primary ?disabled=${tab.disabled} part="tab-ripple"></ft-ripple>
71
+ <ft-icon class="ft-tabs--tab-icon" part="tab-icon" variant="${tab.iconVariant}">
72
+ ${tab.icon}
73
+ </ft-icon>
74
+ <ft-typography class="ft-tabs--tab-label"
75
+ part="tab-label"
76
+ variant="${FtTypographyVariants.body2}">
77
+ ${tab.label}
78
+ </ft-typography>
79
+ </button>
55
80
  `)}
56
- </mwc-tab-bar>
57
- <div class="ft-tabs--content">
58
- <div class="ft-tabs--resizable">
59
- <slot @slotchange=${this.onContentChange}></slot>
60
- </div>
81
+ <div class="ft-tabs--active-tab-indicator" part="active-tab-indicator"></div>
82
+ </div>
83
+ <div class="ft-tabs--content" part="content">
84
+ <slot @slotchange=${this.onContentChange}></slot>
61
85
  </div>
62
86
  </div>
63
87
  `;
64
88
  }
65
- onTabChange(e) {
66
- this.activeIndex = e.detail.index;
67
- }
68
89
  onContentChange(e) {
69
90
  const slot = e.composedPath()[0];
70
- this.tabs = slot.assignedElements().map(n => n);
71
- this.resizeObserver.disconnect();
72
- this.tabs.forEach(tab => {
73
- this.resizeObserver.observe(tab);
74
- tab.addEventListener("updated", () => this.updateDebouncer.run(() => this.requestUpdate()));
91
+ this.ftTabs = slot.assignedElements().map(n => n);
92
+ this.ftTabs.forEach(tab => {
93
+ tab.removeEventListener("updated", this.scheduleRequestUpdate);
94
+ tab.addEventListener("updated", this.scheduleRequestUpdate);
75
95
  });
96
+ this.updateTabs();
76
97
  }
77
98
  updated(props) {
78
99
  super.updated(props);
79
100
  if (props.has("tabs") || props.has("activeIndex")) {
80
- this.placeTabs();
101
+ this.updateTabs();
81
102
  }
82
103
  if (props.has("activeIndex")) {
83
104
  this.dispatchEvent(new IndexChangeEvent(this.activeIndex));
84
105
  }
85
106
  }
86
- placeTabs() {
87
- this.tabs.forEach((tab, index) => {
88
- tab.visible = index == this.activeIndex;
89
- });
90
- this.resize();
107
+ contentAvailableCallback(props) {
108
+ super.contentAvailableCallback(props);
109
+ if (this.tabsContainer) {
110
+ this.resizeObserver.observe(this.tabsContainer);
111
+ }
112
+ this.placeIndicator();
91
113
  }
92
- resize() {
93
- this.resizeDebouncer.run(() => {
94
- if (this.resizable && this.currentTab) {
95
- this.resizable.style.height = this.currentTab.scrollHeight + "px";
114
+ placeIndicator() {
115
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
116
+ if (this.activeTabIndicator) {
117
+ this.activeTabIndicator.style.width = ((_b = (_a = this.activeTab) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : 0) + "px";
118
+ this.activeTabIndicator.style.left = ((_d = (_c = this.activeTab) === null || _c === void 0 ? void 0 : _c.offsetLeft) !== null && _d !== void 0 ? _d : 0) + "px";
119
+ if (this.contentBefore) {
120
+ this.activeTabIndicator.style.top = ((_f = (_e = this.activeTab) === null || _e === void 0 ? void 0 : _e.offsetTop) !== null && _f !== void 0 ? _f : 0) + "px";
121
+ }
122
+ else {
123
+ this.activeTabIndicator.style.top = (((_h = (_g = this.activeTab) === null || _g === void 0 ? void 0 : _g.offsetTop) !== null && _h !== void 0 ? _h : 0) + ((_k = (_j = this.activeTab) === null || _j === void 0 ? void 0 : _j.clientHeight) !== null && _k !== void 0 ? _k : 0) - this.activeTabIndicator.clientHeight) + "px";
96
124
  }
125
+ }
126
+ }
127
+ updateTabs() {
128
+ this.ftTabs.forEach((tab, index) => {
129
+ tab.active = index == this.activeIndex;
97
130
  });
98
131
  }
99
132
  }
100
133
  FtTabs.elementDefinitions = {
101
134
  "ft-tab": FtTab,
102
- "mwc-tab": Tab,
103
- "mwc-tab-bar": TabBar,
135
+ "ft-ripple": FtRipple,
136
+ "ft-typography": FtTypography,
137
+ "ft-icon": FtIcon,
104
138
  };
105
139
  // language=CSS
106
140
  FtTabs.styles = css `
@@ -115,29 +149,81 @@ FtTabs.styles = css `
115
149
  flex-direction: column-reverse;
116
150
  }
117
151
 
118
- mwc-tab-bar {
119
- display: block;
152
+ [role="tablist"] {
120
153
  flex-shrink: 0;
121
154
  flex-grow: 0;
122
- --mdc-theme-primary: ${FtTabsCssVariables.colorPrimary};
123
- --mdc-tab-color-default: ${FtTabsCssVariables.colorOnSurfaceMedium};
155
+ display: flex;
156
+ position: relative;
157
+ flex-wrap: wrap;
124
158
  }
125
159
 
126
- .ft-tabs--content {
127
- flex-shrink: 1;
128
- flex-grow: 1;
129
- overflow: auto;
160
+ .ft-tabs--align-right {
161
+ justify-content: flex-end;
130
162
  }
131
163
 
132
- .ft-tabs--resizable {
164
+ .ft-tabs--align-justify {
165
+ justify-content: space-evenly;
166
+ }
167
+
168
+ [role='tab'] {
133
169
  position: relative;
170
+ display: flex;
134
171
  overflow: hidden;
172
+ flex-direction: column;
173
+ align-items: center;
174
+ justify-content: center;
175
+ padding: 8px 16px;
176
+ background: none;
177
+ box-shadow: 0px 0px 0px transparent;
178
+ border: 0px solid transparent;
179
+ text-shadow: 0px 0px 0px transparent;
180
+ cursor: pointer;
181
+ outline: none;
182
+ font-size: 16px;
183
+ line-height: 1;
184
+ color: ${FtTabsCssVariables.colorOnSurfaceMedium};
185
+ }
186
+
187
+ [role='tab'][aria-selected='true'] {
188
+ color: ${FtTabsCssVariables.colorPrimary};
189
+ }
190
+
191
+ [role='tab'][disabled] {
192
+ color: ${FtTabsCssVariables.colorOnSurfaceDisabled};
193
+ }
194
+
195
+ .ft-tabs--align-justify [role='tab'] {
196
+ flex-grow: 1;
197
+ }
198
+
199
+ [role='tab'] .ft-tabs--tab-label {
135
200
  width: 100%;
201
+ white-space: nowrap;
202
+ overflow: hidden;
203
+ text-overflow: ellipsis;
136
204
  }
137
205
 
138
- mwc-tab[disabled] {
139
- pointer-events: none;
140
- --mdc-tab-color-default: ${FtTabsCssVariables.colorOnSurfaceDisabled};
206
+ [role='tab']:not(.ft-tabs--tab-with-label) .ft-tabs--tab-label,
207
+ .ft-tabs--dense .ft-tabs--tab-with-icon .ft-tabs--tab-label {
208
+ display: none;
209
+ }
210
+
211
+ [role='tab']:not(.ft-tabs--tab-with-icon) .ft-tabs--tab-icon {
212
+ display: none;
213
+ }
214
+
215
+ .ft-tabs--active-tab-indicator {
216
+ position: absolute;
217
+ height: 3px;
218
+ border-radius: 2px;
219
+ background-color: ${FtTabsCssVariables.colorPrimary};
220
+ transition: width 200ms ease, left 200ms ease, top 200ms ease;
221
+ }
222
+
223
+ .ft-tabs--content {
224
+ flex-shrink: 1;
225
+ flex-grow: 1;
226
+ overflow: auto;
141
227
  }
142
228
  `;
143
229
  __decorate([
@@ -146,13 +232,22 @@ __decorate([
146
232
  __decorate([
147
233
  property({ type: Boolean })
148
234
  ], FtTabs.prototype, "contentBefore", void 0);
235
+ __decorate([
236
+ property()
237
+ ], FtTabs.prototype, "alignTabs", void 0);
149
238
  __decorate([
150
239
  state()
151
- ], FtTabs.prototype, "tabs", void 0);
240
+ ], FtTabs.prototype, "ftTabs", void 0);
241
+ __decorate([
242
+ query("[role='tablist']")
243
+ ], FtTabs.prototype, "tabsContainer", void 0);
244
+ __decorate([
245
+ query("[aria-selected='true']")
246
+ ], FtTabs.prototype, "activeTab", void 0);
247
+ __decorate([
248
+ query(".ft-tabs--active-tab-indicator")
249
+ ], FtTabs.prototype, "activeTabIndicator", void 0);
152
250
  __decorate([
153
251
  property({ type: Number, reflect: true })
154
252
  ], FtTabs.prototype, "activeIndex", void 0);
155
- __decorate([
156
- query(".ft-tabs--resizable")
157
- ], FtTabs.prototype, "resizable", void 0);
158
253
  //# sourceMappingURL=ft-tabs.js.map