@spectric/ui 0.0.16 → 0.0.18
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/classes/DisposibleElement.d.ts +1 -0
- package/dist/classes/index.d.ts +2 -0
- package/dist/components/ThemeProvider.d.ts +1 -0
- package/dist/components/color_picker/ColorPicker.d.ts +59 -0
- package/dist/components/color_picker/index.d.ts +0 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/input.d.ts +3 -1
- package/dist/components/pagination/pagination.d.ts +1 -1
- package/dist/components/table/body.d.ts +2 -2
- package/dist/components/table/cell.d.ts +2 -2
- package/dist/components/table/header.d.ts +9 -3
- package/dist/components/table/table.d.ts +18 -6
- package/dist/components/table/virtualBody.d.ts +4 -3
- package/dist/components/tooltip/popover.d.ts +85 -0
- package/dist/components/tooltip/tooltip.d.ts +14 -15
- package/dist/custom-elements.json +83 -8
- package/dist/index.d.ts +59 -0
- package/dist/index.es.js +2675 -2245
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +189 -135
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/utils/index.d.ts +3 -0
- package/package.json +6 -2
- package/src/classes/DisposibleElement.ts +3 -0
- package/src/classes/index.ts +2 -0
- package/src/components/Button.ts +1 -1
- package/src/components/ThemeProvider.ts +34 -1
- package/src/components/button.css.ts +2 -2
- package/src/components/color_picker/ColorPicker.css +40 -0
- package/src/components/color_picker/ColorPicker.ts +268 -0
- package/src/components/color_picker/index.ts +1 -0
- package/src/components/index.ts +3 -1
- package/src/components/input.css +10 -2
- package/src/components/input.ts +38 -9
- package/src/components/pagination/pagination.ts +2 -2
- package/src/components/table/__tests__/table.spec.ts +91 -0
- package/src/components/table/body.ts +2 -2
- package/src/components/table/cell.ts +11 -5
- package/src/components/table/header.css +54 -0
- package/src/components/table/header.ts +141 -49
- package/src/components/table/table.css +11 -34
- package/src/components/table/table.ts +51 -17
- package/src/components/table/virtualBody.ts +18 -7
- package/src/components/tooltip/popover.ts +221 -0
- package/src/components/tooltip/tooltip.css +21 -16
- package/src/components/tooltip/tooltip.ts +18 -124
- package/src/index.ts +8 -1
- package/src/stories/fixtures/ExampleContent.ts +4 -3
- package/src/stories/table.stories.ts +6 -7
- package/src/utils/index.ts +3 -0
|
@@ -34,6 +34,10 @@ export enum TableSortDirection {
|
|
|
34
34
|
export type TableSortDirectionTypes = `${TableSortDirection}`
|
|
35
35
|
export type ColumnSettings<T> = {
|
|
36
36
|
width?: number
|
|
37
|
+
/**
|
|
38
|
+
* Enabled/disables resizing by dragging column header Default true
|
|
39
|
+
*/
|
|
40
|
+
allowResize?: boolean
|
|
37
41
|
whiteSpace?: "nowrap";
|
|
38
42
|
hidden?: boolean
|
|
39
43
|
sortable?: boolean
|
|
@@ -47,7 +51,7 @@ export type ColumnSettings<T> = {
|
|
|
47
51
|
/**
|
|
48
52
|
* Render function to render a table cell for displaying custom html
|
|
49
53
|
*/
|
|
50
|
-
render?: (row: T, index: number, table:
|
|
54
|
+
render?: (row: T, index: number, table: SpectricTableElement<T>) => DomRenderable
|
|
51
55
|
/**
|
|
52
56
|
* Custom comparator function for sorting
|
|
53
57
|
*/
|
|
@@ -57,6 +61,7 @@ export type TableSelectOptionsTypes = `${TableSelectOptions}`
|
|
|
57
61
|
export interface TableDataOptions<T> {
|
|
58
62
|
pagination?: PaginationProps
|
|
59
63
|
columns: ColumnSettings<T>[]
|
|
64
|
+
sortOrder?: string[]
|
|
60
65
|
}
|
|
61
66
|
interface TableProps<T> extends TableDataOptions<T> {
|
|
62
67
|
data: T[]
|
|
@@ -65,7 +70,7 @@ interface TableProps<T> extends TableDataOptions<T> {
|
|
|
65
70
|
rowHeight?: number
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
type DomEvent<T> = Event & {
|
|
73
|
+
export type DomEvent<T> = Event & {
|
|
69
74
|
target: T
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
@@ -74,7 +79,7 @@ type DomEvent<T> = Event & {
|
|
|
74
79
|
*
|
|
75
80
|
*/
|
|
76
81
|
@customElement(TableElementTag)
|
|
77
|
-
export class
|
|
82
|
+
export class SpectricTableElement<T = any> extends LitElement implements TableProps<T> {
|
|
78
83
|
@property({ type: Array, attribute: false })
|
|
79
84
|
data: T[] = [];
|
|
80
85
|
@property({ type: Object, attribute: false })
|
|
@@ -86,15 +91,23 @@ export class TableElement<T> extends LitElement implements TableProps<T> {
|
|
|
86
91
|
@property({ type: String, reflect: true })
|
|
87
92
|
sort: TableSortOptionTypes = TableSortOption.single;
|
|
88
93
|
|
|
94
|
+
@property({ type: Array, reflect: false })
|
|
95
|
+
sortOrder: string[] = [];
|
|
89
96
|
/**
|
|
90
97
|
* Needed for virtualization
|
|
91
98
|
*/
|
|
92
99
|
@property({ type: Number, reflect: true })
|
|
93
100
|
rowHeight: number = 25;
|
|
101
|
+
/**
|
|
102
|
+
* Needed for virtualization
|
|
103
|
+
*/
|
|
104
|
+
@property({ type: Number, reflect: true })
|
|
105
|
+
fontSize: number = 16;
|
|
94
106
|
|
|
95
107
|
static getDefaultDataSorterAndPaginatior<T>(data: T[]) {
|
|
96
108
|
return (props: TableDataOptions<T>) => {
|
|
97
|
-
let sorts = props.columns.filter(column => column.sortable && column.sortDirection && column.sortDirection !== TableSortDirection.none)
|
|
109
|
+
//let sorts = props.columns.filter(column => column.sortable && column.sortDirection && column.sortDirection !== TableSortDirection.none)
|
|
110
|
+
let sorts = (props.sortOrder || []).map(key => props.columns.find(col => col.key === key) as ColumnSettings<T>)
|
|
98
111
|
let rows = [...data] // Need to copy the array to prevent mutating the ordering of the original data
|
|
99
112
|
if (sorts.length) {
|
|
100
113
|
let sortChain = createSortChain(sorts)
|
|
@@ -136,7 +149,7 @@ export class TableElement<T> extends LitElement implements TableProps<T> {
|
|
|
136
149
|
private _handleSortChange = (e: CustomEvent<ColumnSettings<T>>) => {
|
|
137
150
|
let columnSetting = e.detail
|
|
138
151
|
let column = this.columns.find(col => col.key == columnSetting.key)
|
|
139
|
-
if (!column) {
|
|
152
|
+
if (!column || column.key === undefined) {
|
|
140
153
|
console.warn("Unable to find sort column")
|
|
141
154
|
return
|
|
142
155
|
}
|
|
@@ -145,15 +158,34 @@ export class TableElement<T> extends LitElement implements TableProps<T> {
|
|
|
145
158
|
this.columns.forEach(col => {
|
|
146
159
|
col.sortDirection = TableSortDirection.none
|
|
147
160
|
})
|
|
161
|
+
this.sortOrder = []
|
|
148
162
|
}
|
|
149
163
|
column.sortDirection = columnSetting.sortDirection;
|
|
164
|
+
if (!column.sortDirection || column.sortDirection === "none") {
|
|
165
|
+
let index = this.sortOrder.findIndex(col => col === column.key)
|
|
166
|
+
if (index !== -1) {
|
|
167
|
+
this.sortOrder.splice(index, 1)
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
let index = this.sortOrder.findIndex(col => col === column.key)
|
|
171
|
+
if (index === -1) {
|
|
172
|
+
this.sortOrder = [...this.sortOrder, column.key]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
this.columns = [...this.columns]
|
|
176
|
+
this._emitChange()
|
|
177
|
+
}
|
|
178
|
+
private _handleColumnResize = (e: CustomEvent<ColumnSettings<T>>) => {
|
|
179
|
+
let columnSetting = e.detail
|
|
180
|
+
let index = this.columns.findIndex(col => col.key == columnSetting.key)
|
|
181
|
+
this.columns[index] = { ...this.columns[index] }
|
|
150
182
|
this.columns = [...this.columns]
|
|
151
183
|
this._emitChange()
|
|
152
184
|
}
|
|
153
185
|
|
|
154
186
|
private _emitChange = () => {
|
|
155
|
-
let { pagination, columns } = this
|
|
156
|
-
this.dispatchEvent(new CustomEvent<TableDataOptions<T>>("change", { detail: { pagination, columns } }))
|
|
187
|
+
let { pagination, columns, sortOrder } = this
|
|
188
|
+
this.dispatchEvent(new CustomEvent<TableDataOptions<T>>("change", { detail: { pagination, columns, sortOrder } }))
|
|
157
189
|
}
|
|
158
190
|
//@ts-ignore
|
|
159
191
|
private __DO_NOT_USE_filter = () => {
|
|
@@ -199,7 +231,7 @@ export class TableElement<T> extends LitElement implements TableProps<T> {
|
|
|
199
231
|
render: (row) => {
|
|
200
232
|
let container = document.createElement("div")
|
|
201
233
|
let checked = this.selected.includes(row)
|
|
202
|
-
let template = html`<spectric-input variant="checkbox" class="table-checkbox-${this.select}" ${spreadProps({ checked })} @change=${(e: DomEvent<HTMLInputElement>) => {
|
|
234
|
+
let template = html`<spectric-input variant="checkbox" class="table-checkbox-${this.select}" .helperText=${""} ${spreadProps({ checked })} @change=${(e: DomEvent<HTMLInputElement>) => {
|
|
203
235
|
e.stopPropagation()
|
|
204
236
|
let index = this.selected.findIndex(value => value === row)
|
|
205
237
|
if (e.target.checked && index !== -1) {
|
|
@@ -225,14 +257,15 @@ export class TableElement<T> extends LitElement implements TableProps<T> {
|
|
|
225
257
|
})
|
|
226
258
|
}
|
|
227
259
|
const tdBorderAndPadding = 4;
|
|
260
|
+
let rowHeight = this.rowHeight - tdBorderAndPadding;
|
|
261
|
+
if (rowHeight < this.fontSize + tdBorderAndPadding) {
|
|
262
|
+
rowHeight = this.fontSize + tdBorderAndPadding
|
|
263
|
+
}
|
|
228
264
|
return html`
|
|
229
|
-
<div class="table-wrapper" style="--rowHeight:${this.rowHeight
|
|
265
|
+
<div class="table-wrapper" style="--rowHeight:${rowHeight}px;--fontSize:${this.fontSize}px;--lineClamp:${Math.floor(rowHeight / this.fontSize)}">
|
|
230
266
|
<div role="table">
|
|
231
|
-
<spectric-table-header .columns=${columns} @sortChange=${this._handleSortChange}></spectric-table-header>
|
|
232
|
-
|
|
233
|
-
html`<spectric-table-virtual-body .columns=${columns} .data=${this.data} .table=${this} .rowHeight=${this.rowHeight}></spectric-table-virtual-body>` :
|
|
234
|
-
html`<spectric-table-body .columns=${columns} .data=${this.data} .table=${this}></spectric-table-body>`
|
|
235
|
-
}
|
|
267
|
+
<spectric-table-header .columns=${columns} @sortChange=${this._handleSortChange} @columnResize=${this._handleColumnResize}></spectric-table-header>
|
|
268
|
+
<spectric-table-virtual-body .columns=${columns} .data=${this.data} .table=${this} .rowHeight=${rowHeight}></spectric-table-virtual-body>
|
|
236
269
|
</div>
|
|
237
270
|
</div>
|
|
238
271
|
${this.pagination ? html`<spectric-pagination ${spreadProps(this.pagination)} @change=${this._handlePaginationChange}></spectric-pagination>` : null}
|
|
@@ -245,11 +278,12 @@ interface TableEvents {
|
|
|
245
278
|
'paginationChange': (event: CustomEvent<PaginationChangeProps>) => void;
|
|
246
279
|
'filter': (event: CustomEvent<FilterEvent<any>>) => void;
|
|
247
280
|
'sortChange': (event: CustomEvent<ColumnSettings<any>>) => void;
|
|
281
|
+
'selected': (event: CustomEvent<any[]>) => void;
|
|
248
282
|
}
|
|
249
283
|
|
|
250
284
|
declare global {
|
|
251
285
|
interface HTMLElementTagNameMap {
|
|
252
|
-
[TableElementTag]: HTMLElementTagWithEvents<
|
|
286
|
+
[TableElementTag]: HTMLElementTagWithEvents<SpectricTableElement<any>, TableEvents>
|
|
253
287
|
|
|
254
288
|
}
|
|
255
289
|
namespace JSX {
|
|
@@ -257,7 +291,7 @@ declare global {
|
|
|
257
291
|
/**
|
|
258
292
|
* @see {@link TableElement}
|
|
259
293
|
*/
|
|
260
|
-
[TableElementTag]: ReactElementWithPropsAndEvents<
|
|
294
|
+
[TableElementTag]: ReactElementWithPropsAndEvents<SpectricTableElement<any>, TableProps<any>, TableEvents>;
|
|
261
295
|
}
|
|
262
296
|
}
|
|
263
297
|
namespace React {
|
|
@@ -266,7 +300,7 @@ declare global {
|
|
|
266
300
|
/**
|
|
267
301
|
* @see {@link TableElement}
|
|
268
302
|
*/
|
|
269
|
-
[TableElementTag]: ReactElementWithPropsAndEvents<
|
|
303
|
+
[TableElementTag]: ReactElementWithPropsAndEvents<SpectricTableElement<any>, TableProps<any>, TableEvents>
|
|
270
304
|
}
|
|
271
305
|
}
|
|
272
306
|
}
|
|
@@ -3,7 +3,7 @@ import { customElement, property, } from 'lit/decorators.js';
|
|
|
3
3
|
import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from '../types';
|
|
4
4
|
export const TableBodyElementTag = "spectric-table-virtual-body"
|
|
5
5
|
import "./cell"
|
|
6
|
-
import { ColumnSettings,
|
|
6
|
+
import { ColumnSettings, SpectricTableElement } from './table';
|
|
7
7
|
import { repeat } from 'lit/directives/repeat.js';
|
|
8
8
|
import { DisposableElement } from '../../classes/DisposibleElement';
|
|
9
9
|
import "./virtualBody.css"
|
|
@@ -29,7 +29,8 @@ export class TableVirtualBodyElement<T> extends DisposableElement implements Bod
|
|
|
29
29
|
@property({ type: Number, state: true })
|
|
30
30
|
startIndex: number = 0
|
|
31
31
|
|
|
32
|
-
table!:
|
|
32
|
+
table!: SpectricTableElement<T>
|
|
33
|
+
columnsMeasured: boolean = false;
|
|
33
34
|
constructor() {
|
|
34
35
|
super()
|
|
35
36
|
this.addDisposableListener(() => this.table.querySelector(".table-wrapper")!, "scroll", () => {
|
|
@@ -39,12 +40,22 @@ export class TableVirtualBodyElement<T> extends DisposableElement implements Bod
|
|
|
39
40
|
})
|
|
40
41
|
})
|
|
41
42
|
}
|
|
42
|
-
protected
|
|
43
|
-
this.
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
protected updated(): void {
|
|
44
|
+
if (this.columnsMeasured === false) {
|
|
45
|
+
let tr = this.querySelector("tr");
|
|
46
|
+
let cells = tr?.querySelectorAll("spectric-table-cell td")
|
|
47
|
+
if (tr && cells && cells.length) {
|
|
48
|
+
this.columns.forEach((col, index) => {
|
|
49
|
+
if (!col.width || col.width === 0) {
|
|
50
|
+
let rect = cells[index].getBoundingClientRect()
|
|
51
|
+
if (rect.width > 0) {
|
|
52
|
+
this.columnsMeasured = true
|
|
53
|
+
col.width = rect.width
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})
|
|
46
57
|
}
|
|
47
|
-
}
|
|
58
|
+
}
|
|
48
59
|
}
|
|
49
60
|
protected createRenderRoot(): HTMLElement | DocumentFragment {
|
|
50
61
|
return this
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
|
|
2
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
3
|
+
import "./tooltip.css"
|
|
4
|
+
export const PopoverElementTag = "spectric-popover"
|
|
5
|
+
import { css, CSSResultGroup, html, render } from "lit-element";
|
|
6
|
+
import { DomRenderable } from "../table";
|
|
7
|
+
import { DisposableElement } from '../../classes/DisposibleElement';
|
|
8
|
+
import { SPECTRIC_CSS_VARIABLES } from '../ThemeProvider';
|
|
9
|
+
export type { TooltipProps, TooltipEvents }
|
|
10
|
+
export enum TooltipPostions {
|
|
11
|
+
top = "top",
|
|
12
|
+
bottom = "bottom",
|
|
13
|
+
left = "left",
|
|
14
|
+
right = "right",
|
|
15
|
+
mouse = "mouse"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type TooltipPostionsTypes = `${TooltipPostions}`
|
|
19
|
+
interface TooltipProps {
|
|
20
|
+
/**
|
|
21
|
+
* How long you need to hover before the tooltip displays
|
|
22
|
+
*/
|
|
23
|
+
delay?: number;
|
|
24
|
+
/**
|
|
25
|
+
* How long the fade in animation should run
|
|
26
|
+
*/
|
|
27
|
+
animationDuration?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Tooltip contents
|
|
30
|
+
*/
|
|
31
|
+
text: DomRenderable
|
|
32
|
+
/**
|
|
33
|
+
* Where to anchor the tooltip
|
|
34
|
+
*/
|
|
35
|
+
position?: TooltipPostionsTypes
|
|
36
|
+
/**
|
|
37
|
+
* Sets a max width for the contents you can disable this by setting to 0 or -1
|
|
38
|
+
*/
|
|
39
|
+
maxWidth?: number
|
|
40
|
+
/**
|
|
41
|
+
* Container the tool tip will be attached to.
|
|
42
|
+
*/
|
|
43
|
+
portalTarget?: HTMLElement
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The element that triggers the tooltip. This is used for special cases like in the shadow dom if you want to target a host element instead of the immediate parent element
|
|
47
|
+
*/
|
|
48
|
+
triggerTarget?: HTMLElement
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Spectric tooltip will add a tooltip to any container
|
|
53
|
+
*/
|
|
54
|
+
@customElement(PopoverElementTag)
|
|
55
|
+
export class PopoverElement extends DisposableElement implements TooltipProps {
|
|
56
|
+
@property({ type: Number, reflect: true })
|
|
57
|
+
delay: number = 100;
|
|
58
|
+
@property({ type: Number, reflect: true })
|
|
59
|
+
animationDuration: number = 0;
|
|
60
|
+
@property({ type: String, reflect: false })
|
|
61
|
+
text: DomRenderable = "";
|
|
62
|
+
@property({ type: String, reflect: true })
|
|
63
|
+
position: TooltipPostionsTypes = "right";
|
|
64
|
+
@property({ type: Number, reflect: true })
|
|
65
|
+
maxWidth?: number = 300;
|
|
66
|
+
protected portalElement!: HTMLDivElement
|
|
67
|
+
protected mouseLocation?: { left: number; top: number; };
|
|
68
|
+
static styles?: CSSResultGroup | undefined = css`:host{max-height: 0px;
|
|
69
|
+
max-width: 0px;
|
|
70
|
+
display: none;
|
|
71
|
+
pointer-events:none;}`;
|
|
72
|
+
@property({ attribute: false })
|
|
73
|
+
portalTarget: HTMLElement = document.body
|
|
74
|
+
protected timer?: number;
|
|
75
|
+
protected open: boolean = false;
|
|
76
|
+
protected mouseframe?: number;
|
|
77
|
+
/**
|
|
78
|
+
* @default parentElement
|
|
79
|
+
*/
|
|
80
|
+
@property({ attribute: false })
|
|
81
|
+
triggerTarget!: HTMLElement;
|
|
82
|
+
protected get target() {
|
|
83
|
+
return this.triggerTarget || this.parentElement
|
|
84
|
+
}
|
|
85
|
+
constructor() {
|
|
86
|
+
super()
|
|
87
|
+
/**
|
|
88
|
+
* Public method to trigger showing the tooltip programatically
|
|
89
|
+
*/
|
|
90
|
+
this.showPopover = this.showPopover.bind(this)
|
|
91
|
+
this.hidePopover = this.hidePopover.bind(this)
|
|
92
|
+
this.addDisposableListener(() => this.target, "mousemove", this._getMousePosition)
|
|
93
|
+
}
|
|
94
|
+
connectedCallback(): void {
|
|
95
|
+
super.connectedCallback()
|
|
96
|
+
this.portalElement = document.createElement("div")
|
|
97
|
+
this.portalElement.className = "spectric-tooltip-portal"
|
|
98
|
+
}
|
|
99
|
+
disconnectedCallback(): void {
|
|
100
|
+
super.disconnectedCallback();
|
|
101
|
+
this.portalElement.remove()
|
|
102
|
+
//@ts-ignore
|
|
103
|
+
this.portalElement = undefined
|
|
104
|
+
}
|
|
105
|
+
protected _getMousePosition = (ev: MouseEvent) => {
|
|
106
|
+
this.mouseLocation = {
|
|
107
|
+
left: ev.clientX,
|
|
108
|
+
top: ev.clientY
|
|
109
|
+
}
|
|
110
|
+
if (this.position == "mouse" && this.open && !this.mouseframe) {
|
|
111
|
+
this.mouseframe = requestAnimationFrame(() => this.positionTooltip())
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Public method to trigger hide the tooltip programatically
|
|
116
|
+
*/
|
|
117
|
+
public hidePopover() {
|
|
118
|
+
if (this.timer) {
|
|
119
|
+
clearTimeout(this.timer)
|
|
120
|
+
}
|
|
121
|
+
this.open = false
|
|
122
|
+
this.portalElement.remove()
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Public method to trigger showing the tooltip programatically
|
|
126
|
+
*/
|
|
127
|
+
public async showPopover() {
|
|
128
|
+
if (this.timer) {
|
|
129
|
+
clearTimeout(this.timer)
|
|
130
|
+
}
|
|
131
|
+
await new Promise(resolve => {
|
|
132
|
+
this.timer = window.setTimeout(resolve, this.delay)
|
|
133
|
+
})
|
|
134
|
+
this.portalElement.className = `spectric-popover-portal ${this.position}`
|
|
135
|
+
const tooltip = html`<div class="tooltip-container">
|
|
136
|
+
<span class="tooltip-caret"></span>
|
|
137
|
+
<div class="tooltip-content">${this.text}</div>
|
|
138
|
+
</div>`
|
|
139
|
+
render(tooltip, this.portalElement)
|
|
140
|
+
//with the delay it is possible that the triggering element was removed or hidden lets check that it is visible
|
|
141
|
+
if (!this.target || !this.target.checkVisibility()) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
//We need to append our tooltip and let the css updates apply before we can take measurements
|
|
145
|
+
this.portalTarget.appendChild(this.portalElement)
|
|
146
|
+
this.open = true
|
|
147
|
+
requestAnimationFrame(this.positionTooltip)
|
|
148
|
+
}
|
|
149
|
+
protected applyStyle = (style: Record<string, string>) => {
|
|
150
|
+
Object.entries(style).forEach(([prop, value]) => {
|
|
151
|
+
this.portalElement.style.setProperty(prop, value)
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
protected positionTooltip = () => {
|
|
155
|
+
if (!this.target) {
|
|
156
|
+
//We got detached before the animation frame completed
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
//Since we are attaching to a body we need to send the correct styles from the target portion of the dom to the portal element
|
|
160
|
+
let computedStyle = getComputedStyle(this)
|
|
161
|
+
|
|
162
|
+
let styles: Record<string, string> = {
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
SPECTRIC_CSS_VARIABLES.forEach(v => {
|
|
166
|
+
styles[v] = computedStyle.getPropertyValue(v)
|
|
167
|
+
})
|
|
168
|
+
const bounds = this.target.getBoundingClientRect()
|
|
169
|
+
const portalBounds = this.portalElement.getBoundingClientRect()
|
|
170
|
+
if (this.target !== document.body) {
|
|
171
|
+
if (this.maxWidth && this.maxWidth > 0) {
|
|
172
|
+
portalBounds.width = Math.min(portalBounds.width, this.maxWidth)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
let location: {
|
|
176
|
+
left: string;
|
|
177
|
+
top: string;
|
|
178
|
+
};
|
|
179
|
+
if (this.position === "mouse" && this.mouseLocation) {
|
|
180
|
+
this.mouseframe = undefined
|
|
181
|
+
location = { left: this.mouseLocation.left + 10 + "px", top: this.mouseLocation.top - (portalBounds.height / 2) + "px" }
|
|
182
|
+
} else if (this.position === "top") {
|
|
183
|
+
location = {
|
|
184
|
+
top: bounds.top - portalBounds.height + "px",
|
|
185
|
+
left: (bounds.left + (bounds.width / 2)) - (portalBounds.width / 2) + "px"
|
|
186
|
+
}
|
|
187
|
+
} else if (this.position === "bottom") {
|
|
188
|
+
location = {
|
|
189
|
+
top: bounds.bottom + "px",
|
|
190
|
+
left: (bounds.left + (bounds.width / 2)) - (portalBounds.width / 2) + "px"
|
|
191
|
+
}
|
|
192
|
+
} else if (this.position === "left") {
|
|
193
|
+
location = {
|
|
194
|
+
top: Math.max(0, bounds.top + bounds.height / 2 - portalBounds.height / 2) + "px",
|
|
195
|
+
left: bounds.left - (portalBounds.width) + "px"
|
|
196
|
+
}
|
|
197
|
+
} else if (this.position === "right") {
|
|
198
|
+
location = {
|
|
199
|
+
top: Math.max(0, bounds.top + bounds.height / 2 - portalBounds.height / 2) + "px",
|
|
200
|
+
left: bounds.right + "px"
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
location = { left: "0px", top: "0px" };
|
|
204
|
+
console.error("Unknown position... Maybe we sould implement auto?")
|
|
205
|
+
}
|
|
206
|
+
this.applyStyle({ ...styles, ...location })
|
|
207
|
+
if (this.position !== "mouse") {
|
|
208
|
+
this.portalElement.animate({ opacity: [0, 1] }, { duration: this.animationDuration })
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
protected render() {
|
|
212
|
+
//We don't need to render anything here this is just a placeholder element the content is displayed in a portal attached to the body.
|
|
213
|
+
// See showTooltip for the real rendering
|
|
214
|
+
return html`<!-- ToolTip -->`
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
interface TooltipEvents {
|
|
219
|
+
//'open': (event: CustomEvent<{ pagination: PaginationChangeProps }>) => void;
|
|
220
|
+
//'filter': (event: CustomEvent<FilterEvent>) => void;
|
|
221
|
+
}
|
|
@@ -1,34 +1,39 @@
|
|
|
1
|
-
.spectric-
|
|
1
|
+
.spectric-popover-portal{
|
|
2
2
|
position: fixed;
|
|
3
3
|
z-index: 9999;
|
|
4
|
+
--spectric-tooltip-background-color: var(--spectric-tooltip-background, var(--spectric-background,#000000));
|
|
5
|
+
--spectric-tooltip-text-color: var(--spectric-tooltip-text, var(--spectric-text-primary, white));
|
|
6
|
+
--spectric-tooltip-accent-color: var(--spectric-tooltip-accent, var(--spectric-primary, #1ea7fd));
|
|
7
|
+
}
|
|
8
|
+
.spectric-popover-portal.spectric-tooltip-portal{
|
|
4
9
|
pointer-events: none;
|
|
5
|
-
--spectric-tooltip-background: color-mix(in srgb,var(--spectric-background-inverse,#f4f4f4) 100%,var(--spectric-primary,#1ea7fd) 90%)
|
|
6
10
|
}
|
|
7
|
-
.spectric-
|
|
11
|
+
.spectric-popover-portal .tooltip-container{
|
|
8
12
|
display: flex;
|
|
9
13
|
justify-content: center;
|
|
10
14
|
align-items: center;
|
|
11
15
|
}
|
|
12
|
-
.spectric-
|
|
16
|
+
.spectric-popover-portal.top .tooltip-container{
|
|
13
17
|
flex-direction: column-reverse;
|
|
14
18
|
}
|
|
15
|
-
.spectric-
|
|
19
|
+
.spectric-popover-portal.bottom .tooltip-container{
|
|
16
20
|
flex-direction: column;
|
|
17
21
|
}
|
|
18
|
-
.spectric-
|
|
22
|
+
.spectric-popover-portal.left .tooltip-container{
|
|
19
23
|
flex-direction: row-reverse;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
.spectric-
|
|
23
|
-
background: var(--spectric-tooltip-background);
|
|
26
|
+
.spectric-popover-portal .tooltip-content{
|
|
27
|
+
background: var(--spectric-tooltip-background-color);
|
|
24
28
|
border-radius: var(--spectric-border-radius,.4em);
|
|
25
|
-
|
|
29
|
+
border: 1px solid color-mix(in srgb, var(--spectric-tooltip-background-color) 90%, var(--spectric-tooltip-accent-color) 90%);
|
|
30
|
+
box-shadow: 0 0 .01em .01em color-mix(in srgb, var(--spectric-tooltip-accent-color) 90%, var(--spectric-text-on-color,#ffffff) 90%);
|
|
26
31
|
padding: .2em;
|
|
27
|
-
color:var(--spectric-text-
|
|
32
|
+
color:var(--spectric-tooltip-text-color);
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
.spectric-
|
|
31
|
-
background:var(--spectric-tooltip-background);
|
|
35
|
+
.spectric-popover-portal .tooltip-caret{
|
|
36
|
+
background:color-mix(in srgb, var(--spectric-tooltip-background-color) 90%, var(--spectric-tooltip-accent-color) 90%);
|
|
32
37
|
}
|
|
33
38
|
.spectric-tooltip-portal.top .tooltip-caret,.spectric-tooltip-portal.bottom .tooltip-caret{
|
|
34
39
|
inline-size: .75rem;
|
|
@@ -38,15 +43,15 @@
|
|
|
38
43
|
inline-size: .375rem;
|
|
39
44
|
block-size: .75rem;
|
|
40
45
|
}
|
|
41
|
-
.spectric-
|
|
46
|
+
.spectric-popover-portal.top .tooltip-caret{
|
|
42
47
|
clip-path: polygon(0 0,50% 100%,100% 0);
|
|
43
48
|
}
|
|
44
|
-
.spectric-
|
|
49
|
+
.spectric-popover-portal.bottom .tooltip-caret{
|
|
45
50
|
clip-path: polygon(0 100%,50% 0,100% 100%);
|
|
46
51
|
}
|
|
47
|
-
.spectric-
|
|
52
|
+
.spectric-popover-portal.left .tooltip-caret{
|
|
48
53
|
clip-path: polygon(0 0,100% 50%,0 100%);
|
|
49
54
|
}
|
|
50
|
-
.spectric-
|
|
55
|
+
.spectric-popover-portal.right .tooltip-caret{
|
|
51
56
|
clip-path: polygon(0 50% ,100% 0, 100% 100%);
|
|
52
57
|
}
|