@fluid-topics/ft-tabs 0.2.22 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ readonly uniqueId: string;
18
+ readonly id: string;
19
+ readonly 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,26 @@ 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");
20
+ this.id = "tab-content-" + this.uniqueId;
21
+ this.ariaLabelledBy = "tab-" + this.uniqueId;
32
22
  }
33
23
  render() {
34
24
  return html `
35
25
  <style>
36
26
  :host {
37
- display: ${unsafeCSS(this.visible ? "block" : "none")};
27
+ display: ${unsafeCSS(this.active ? "block" : "none")};
38
28
  }
39
29
  </style>
40
30
  <slot></slot>
@@ -52,10 +42,19 @@ __decorate([
52
42
  __decorate([
53
43
  property({ type: String })
54
44
  ], FtTab.prototype, "icon", void 0);
45
+ __decorate([
46
+ property()
47
+ ], FtTab.prototype, "iconVariant", void 0);
55
48
  __decorate([
56
49
  property({ type: Boolean })
57
- ], FtTab.prototype, "visible", void 0);
50
+ ], FtTab.prototype, "active", void 0);
58
51
  __decorate([
59
52
  property({ type: Boolean })
60
53
  ], FtTab.prototype, "disabled", void 0);
54
+ __decorate([
55
+ property({ type: String, reflect: true })
56
+ ], FtTab.prototype, "id", void 0);
57
+ __decorate([
58
+ property({ type: String, reflect: true, attribute: "aria-labelledby" })
59
+ ], FtTab.prototype, "ariaLabelledBy", void 0);
61
60
  //# 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,103 @@ 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
52
  <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>
53
+ <div class="ft-tabs--align-${this.alignTabs}" role="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
+ id="${tab.ariaLabelledBy}"
64
+ aria-label="${tab.label}"
65
+ aria-selected="${tab.active ? "true" : "false"}"
66
+ tabindex="${tab.disabled ? "-1" : "0"}"
67
+ aria-controls="${tab.id}"
68
+ >
69
+ <ft-ripple primary ?disabled=${tab.disabled}></ft-ripple>
70
+ <ft-icon class="ft-tabs--tab-icon" variant="${tab.iconVariant}">
71
+ ${tab.icon}
72
+ </ft-icon>
73
+ <ft-typography class="ft-tabs--tab-label" variant="${FtTypographyVariants.body2}">
74
+ ${tab.label}
75
+ </ft-typography>
76
+ </button>
55
77
  `)}
56
- </mwc-tab-bar>
78
+ <div class="ft-tabs--active-tab-indicator"></div>
79
+ </div>
57
80
  <div class="ft-tabs--content">
58
- <div class="ft-tabs--resizable">
59
- <slot @slotchange=${this.onContentChange}></slot>
60
- </div>
81
+ <slot @slotchange=${this.onContentChange}></slot>
61
82
  </div>
62
83
  </div>
63
84
  `;
64
85
  }
65
- onTabChange(e) {
66
- this.activeIndex = e.detail.index;
67
- }
68
86
  onContentChange(e) {
69
87
  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()));
88
+ this.ftTabs = slot.assignedElements().map(n => n);
89
+ this.ftTabs.forEach(tab => {
90
+ tab.removeEventListener("updated", this.scheduleRequestUpdate);
91
+ tab.addEventListener("updated", this.scheduleRequestUpdate);
75
92
  });
93
+ this.updateTabs();
76
94
  }
77
95
  updated(props) {
78
96
  super.updated(props);
79
97
  if (props.has("tabs") || props.has("activeIndex")) {
80
- this.placeTabs();
98
+ this.updateTabs();
81
99
  }
82
100
  if (props.has("activeIndex")) {
83
101
  this.dispatchEvent(new IndexChangeEvent(this.activeIndex));
84
102
  }
85
103
  }
86
- placeTabs() {
87
- this.tabs.forEach((tab, index) => {
88
- tab.visible = index == this.activeIndex;
89
- });
90
- this.resize();
104
+ contentAvailableCallback(props) {
105
+ super.contentAvailableCallback(props);
106
+ if (this.tabsContainer) {
107
+ this.resizeObserver.observe(this.tabsContainer);
108
+ }
109
+ this.placeIndicator();
91
110
  }
92
- resize() {
93
- this.resizeDebouncer.run(() => {
94
- if (this.resizable && this.currentTab) {
95
- this.resizable.style.height = this.currentTab.scrollHeight + "px";
111
+ placeIndicator() {
112
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
113
+ if (this.activeTabIndicator) {
114
+ this.activeTabIndicator.style.width = ((_b = (_a = this.activeTab) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : 0) + "px";
115
+ this.activeTabIndicator.style.left = ((_d = (_c = this.activeTab) === null || _c === void 0 ? void 0 : _c.offsetLeft) !== null && _d !== void 0 ? _d : 0) + "px";
116
+ if (this.contentBefore) {
117
+ this.activeTabIndicator.style.top = ((_f = (_e = this.activeTab) === null || _e === void 0 ? void 0 : _e.offsetTop) !== null && _f !== void 0 ? _f : 0) + "px";
118
+ }
119
+ else {
120
+ 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
121
  }
122
+ }
123
+ }
124
+ updateTabs() {
125
+ this.ftTabs.forEach((tab, index) => {
126
+ tab.active = index == this.activeIndex;
97
127
  });
98
128
  }
99
129
  }
100
130
  FtTabs.elementDefinitions = {
101
131
  "ft-tab": FtTab,
102
- "mwc-tab": Tab,
103
- "mwc-tab-bar": TabBar,
132
+ "ft-ripple": FtRipple,
133
+ "ft-typography": FtTypography,
134
+ "ft-icon": FtIcon,
104
135
  };
105
136
  // language=CSS
106
137
  FtTabs.styles = css `
@@ -115,29 +146,81 @@ FtTabs.styles = css `
115
146
  flex-direction: column-reverse;
116
147
  }
117
148
 
118
- mwc-tab-bar {
119
- display: block;
149
+ [role="tablist"] {
120
150
  flex-shrink: 0;
121
151
  flex-grow: 0;
122
- --mdc-theme-primary: ${FtTabsCssVariables.colorPrimary};
123
- --mdc-tab-color-default: ${FtTabsCssVariables.colorOnSurfaceMedium};
152
+ display: flex;
153
+ position: relative;
154
+ flex-wrap: wrap;
124
155
  }
125
156
 
126
- .ft-tabs--content {
127
- flex-shrink: 1;
128
- flex-grow: 1;
129
- overflow: auto;
157
+ .ft-tabs--align-right {
158
+ justify-content: flex-end;
130
159
  }
131
160
 
132
- .ft-tabs--resizable {
161
+ .ft-tabs--align-justify {
162
+ justify-content: space-evenly;
163
+ }
164
+
165
+ [role='tab'] {
133
166
  position: relative;
167
+ display: flex;
134
168
  overflow: hidden;
169
+ flex-direction: column;
170
+ align-items: center;
171
+ justify-content: center;
172
+ padding: 8px 16px;
173
+ background: none;
174
+ box-shadow: 0px 0px 0px transparent;
175
+ border: 0px solid transparent;
176
+ text-shadow: 0px 0px 0px transparent;
177
+ cursor: pointer;
178
+ outline: none;
179
+ font-size: 16px;
180
+ line-height: 1;
181
+ color: ${FtTabsCssVariables.colorOnSurfaceMedium};
182
+ }
183
+
184
+ [role='tab'][aria-selected='true'] {
185
+ color: ${FtTabsCssVariables.colorPrimary};
186
+ }
187
+
188
+ [role='tab'][disabled] {
189
+ color: ${FtTabsCssVariables.colorOnSurfaceDisabled};
190
+ }
191
+
192
+ .ft-tabs--align-justify [role='tab'] {
193
+ flex-grow: 1;
194
+ }
195
+
196
+ [role='tab'] .ft-tabs--tab-label {
135
197
  width: 100%;
198
+ white-space: nowrap;
199
+ overflow: hidden;
200
+ text-overflow: ellipsis;
136
201
  }
137
202
 
138
- mwc-tab[disabled] {
139
- pointer-events: none;
140
- --mdc-tab-color-default: ${FtTabsCssVariables.colorOnSurfaceDisabled};
203
+ [role='tab']:not(.ft-tabs--tab-with-label) .ft-tabs--tab-label,
204
+ .ft-tabs--dense .ft-tabs--tab-with-icon .ft-tabs--tab-label {
205
+ display: none;
206
+ }
207
+
208
+ [role='tab']:not(.ft-tabs--tab-with-icon) .ft-tabs--tab-icon {
209
+ display: none;
210
+ }
211
+
212
+ .ft-tabs--active-tab-indicator {
213
+ position: absolute;
214
+ height: 3px;
215
+ border-radius: 2px;
216
+ background-color: ${FtTabsCssVariables.colorPrimary};
217
+ transition: width 200ms ease, left 200ms ease, top 200ms ease;
218
+ }
219
+
220
+ .ft-tabs--content {
221
+ flex-shrink: 1;
222
+ flex-grow: 1;
223
+ overflow: auto;
141
224
  }
142
225
  `;
143
226
  __decorate([
@@ -146,13 +229,22 @@ __decorate([
146
229
  __decorate([
147
230
  property({ type: Boolean })
148
231
  ], FtTabs.prototype, "contentBefore", void 0);
232
+ __decorate([
233
+ property()
234
+ ], FtTabs.prototype, "alignTabs", void 0);
149
235
  __decorate([
150
236
  state()
151
- ], FtTabs.prototype, "tabs", void 0);
237
+ ], FtTabs.prototype, "ftTabs", void 0);
238
+ __decorate([
239
+ query("[role='tablist']")
240
+ ], FtTabs.prototype, "tabsContainer", void 0);
241
+ __decorate([
242
+ query("[aria-selected='true']")
243
+ ], FtTabs.prototype, "activeTab", void 0);
244
+ __decorate([
245
+ query(".ft-tabs--active-tab-indicator")
246
+ ], FtTabs.prototype, "activeTabIndicator", void 0);
152
247
  __decorate([
153
248
  property({ type: Number, reflect: true })
154
249
  ], FtTabs.prototype, "activeIndex", void 0);
155
- __decorate([
156
- query(".ft-tabs--resizable")
157
- ], FtTabs.prototype, "resizable", void 0);
158
250
  //# sourceMappingURL=ft-tabs.js.map