@spectric/ui 0.0.19 → 0.0.21
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/Banner.d.ts +1 -1
- package/dist/components/dialog/dialog.d.ts +1 -1
- package/dist/components/query_bar/QueryBar.d.ts +1 -1
- package/dist/components/table/cell.d.ts +1 -1
- package/dist/components/table/header.d.ts +2 -1
- package/dist/components/table/table.d.ts +10 -7
- package/dist/custom-elements.json +4 -4
- package/dist/index.es.js +876 -741
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +213 -138
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Banner.ts +46 -31
- package/src/components/dialog/dialog.ts +163 -135
- package/src/components/query_bar/QueryBar.ts +7 -2
- package/src/components/table/__tests__/table.spec.ts +143 -55
- package/src/components/table/cell.ts +185 -146
- package/src/components/table/header.ts +162 -152
- package/src/components/table/table.ts +362 -265
- package/src/components/table/virtualBody.ts +165 -115
- package/src/stories/table.stories.ts +10 -5
package/package.json
CHANGED
package/src/components/Banner.ts
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { html, LitElement } from
|
|
2
|
-
import { StyleInfo, styleMap } from
|
|
3
|
-
import "./Button"
|
|
4
|
-
import { customElement, property } from
|
|
5
|
-
import {
|
|
1
|
+
import { html, LitElement } from "lit";
|
|
2
|
+
import { StyleInfo, styleMap } from "lit/directives/style-map.js";
|
|
3
|
+
import "./Button";
|
|
4
|
+
import { customElement, property } from "lit/decorators.js";
|
|
5
|
+
import {
|
|
6
|
+
HTMLElementTagWithEvents,
|
|
7
|
+
ReactElementWithPropsAndEvents,
|
|
8
|
+
} from "./types";
|
|
6
9
|
|
|
7
10
|
export interface BannerProps {
|
|
8
11
|
/**What is returned in the event details when a banner is dismissed */
|
|
9
|
-
bannerId?: any
|
|
12
|
+
bannerId?: any;
|
|
10
13
|
/** Banner Text to display */
|
|
11
14
|
text?: string;
|
|
12
15
|
/** What background color to use */
|
|
13
16
|
headerStyle?: StyleInfo;
|
|
14
17
|
/** Can be dismissed */
|
|
15
18
|
dismissable?: boolean;
|
|
16
|
-
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
21
|
const DEFAULT_STYLE: StyleInfo = {
|
|
22
22
|
display: "flex",
|
|
23
23
|
alignItems: "center",
|
|
24
24
|
backgroundColor: "black",
|
|
25
25
|
color: "white",
|
|
26
|
-
textAlign: "center"
|
|
27
|
-
}
|
|
26
|
+
textAlign: "center",
|
|
27
|
+
};
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* @slot text - Banner Text content
|
|
31
31
|
*/
|
|
32
|
-
@customElement(
|
|
32
|
+
@customElement("spectric-banner")
|
|
33
33
|
export class SpectricBanner extends LitElement implements BannerProps {
|
|
34
34
|
@property({ type: Boolean, reflect: true })
|
|
35
35
|
dismissable: boolean = false;
|
|
@@ -45,40 +45,47 @@ export class SpectricBanner extends LitElement implements BannerProps {
|
|
|
45
45
|
private _dismissed: boolean = false;
|
|
46
46
|
|
|
47
47
|
private onDismiss = (e: PointerEvent) => {
|
|
48
|
-
e.preventDefault()
|
|
49
|
-
e.stopPropagation()
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
e.stopPropagation();
|
|
50
50
|
const options = {
|
|
51
51
|
bubbles: true,
|
|
52
52
|
composed: true,
|
|
53
53
|
};
|
|
54
54
|
this._dismissed = true;
|
|
55
|
-
let { bannerId, text, headerStyle, dismissable }: BannerProps = this
|
|
56
|
-
this.dispatchEvent(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
let { bannerId, text, headerStyle, dismissable }: BannerProps = this;
|
|
56
|
+
this.dispatchEvent(
|
|
57
|
+
new CustomEvent<BannerProps>("bannerDismissed", {
|
|
58
|
+
detail: { bannerId, text, headerStyle, dismissable },
|
|
59
|
+
...options,
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
};
|
|
61
63
|
protected render(): unknown {
|
|
62
64
|
if (this._dismissed) {
|
|
63
|
-
return
|
|
65
|
+
return;
|
|
64
66
|
}
|
|
65
67
|
return html`
|
|
66
|
-
|
|
67
|
-
<div style="flex-grow:1"
|
|
68
|
-
|
|
69
|
-
</
|
|
68
|
+
<header style=${styleMap({ ...DEFAULT_STYLE, ...this.headerStyle })}>
|
|
69
|
+
<div style="flex-grow:1">
|
|
70
|
+
<slot name="text">${this.text}</slot><slot></slot>
|
|
71
|
+
</div>
|
|
72
|
+
${this.dismissable
|
|
73
|
+
? html`<spectric-button size="small" @click=${this.onDismiss} icon
|
|
74
|
+
>X</spectric-button
|
|
75
|
+
>`
|
|
76
|
+
: null}
|
|
77
|
+
</header>
|
|
70
78
|
`;
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
export interface BannerEventMap {
|
|
75
|
-
|
|
83
|
+
bannerDismissed: (event: CustomEvent<BannerProps>) => void;
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
declare global {
|
|
79
|
-
|
|
80
87
|
interface HTMLElementTagNameMap {
|
|
81
|
-
"spectric-banner": HTMLElementTagWithEvents<SpectricBanner, BannerEventMap
|
|
88
|
+
"spectric-banner": HTMLElementTagWithEvents<SpectricBanner, BannerEventMap>;
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
namespace JSX {
|
|
@@ -86,7 +93,11 @@ declare global {
|
|
|
86
93
|
/**
|
|
87
94
|
* {@link SpectricBanner}
|
|
88
95
|
*/
|
|
89
|
-
"spectric-banner": ReactElementWithPropsAndEvents<
|
|
96
|
+
"spectric-banner": ReactElementWithPropsAndEvents<
|
|
97
|
+
SpectricBanner,
|
|
98
|
+
BannerProps,
|
|
99
|
+
BannerEventMap
|
|
100
|
+
>;
|
|
90
101
|
}
|
|
91
102
|
}
|
|
92
103
|
namespace React {
|
|
@@ -95,8 +106,12 @@ declare global {
|
|
|
95
106
|
/**
|
|
96
107
|
* {@link SpectricBanner}
|
|
97
108
|
*/
|
|
98
|
-
"spectric-banner": ReactElementWithPropsAndEvents<
|
|
109
|
+
"spectric-banner": ReactElementWithPropsAndEvents<
|
|
110
|
+
SpectricBanner,
|
|
111
|
+
BannerProps,
|
|
112
|
+
BannerEventMap
|
|
113
|
+
>;
|
|
99
114
|
}
|
|
100
115
|
}
|
|
101
116
|
}
|
|
102
|
-
}
|
|
117
|
+
}
|
|
@@ -1,163 +1,191 @@
|
|
|
1
|
-
import { CSSResultGroup, html, LitElement, PropertyValues, render } from
|
|
1
|
+
import { CSSResultGroup, html, LitElement, PropertyValues, render } from "lit";
|
|
2
2
|
import "../Button";
|
|
3
|
-
import { customElement, property, queryAsync } from
|
|
4
|
-
import { style } from
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { customElement, property, queryAsync } from "lit/decorators.js";
|
|
4
|
+
import { style } from "./dialog.css";
|
|
5
|
+
import {
|
|
6
|
+
HTMLElementTagWithEvents,
|
|
7
|
+
ReactElementWithPropsAndEvents,
|
|
8
|
+
} from "../types";
|
|
9
|
+
export const DialogElementTag = "spectric-dialog";
|
|
10
|
+
export type { DialogProps as Props, DialogEvents };
|
|
8
11
|
interface DialogProps {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
open?: boolean;
|
|
13
|
+
dismissable?: boolean;
|
|
14
|
+
hideBackdrop?: boolean;
|
|
15
|
+
title?: string;
|
|
16
|
+
closeOnEscape?: boolean;
|
|
17
|
+
closeOnOutsideClick?: boolean;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
20
|
/**
|
|
20
21
|
* Dialog Element
|
|
21
22
|
* @slot title - sets the title of the dialog attribute/property can also be use for setting the title to a string
|
|
22
23
|
*/
|
|
23
24
|
@customElement(DialogElementTag)
|
|
24
25
|
export class DialogElement extends LitElement implements DialogProps {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
static styles: CSSResultGroup = style;
|
|
27
|
+
static display(props: DialogProps, body: any) {
|
|
28
|
+
let dialog = document.createElement(DialogElementTag);
|
|
29
|
+
dialog.addEventListener("close", () => {
|
|
30
|
+
dialog.remove();
|
|
31
|
+
});
|
|
32
|
+
for (let prop in props) {
|
|
33
|
+
//@ts-ignore
|
|
34
|
+
dialog[prop] = props[prop];
|
|
35
|
+
}
|
|
36
|
+
let frag = document.createDocumentFragment();
|
|
37
|
+
render(body, frag);
|
|
38
|
+
dialog.appendChild(frag);
|
|
39
|
+
dialog.open = true;
|
|
40
|
+
document.body.appendChild(dialog);
|
|
41
|
+
return dialog;
|
|
42
|
+
}
|
|
43
|
+
@property({ type: Boolean, reflect: true })
|
|
44
|
+
open: boolean = false;
|
|
45
|
+
|
|
46
|
+
@property({ type: Boolean, reflect: true })
|
|
47
|
+
dismissable: boolean = true;
|
|
48
|
+
|
|
49
|
+
@property({ type: Boolean, reflect: true })
|
|
50
|
+
hideBackdrop: boolean = false;
|
|
51
|
+
|
|
52
|
+
@property({ type: String, reflect: true })
|
|
53
|
+
title: string = "";
|
|
54
|
+
|
|
55
|
+
@property({ type: Boolean, reflect: true })
|
|
56
|
+
closeOnEscape: boolean = true;
|
|
57
|
+
|
|
58
|
+
@property({ type: Boolean, reflect: true })
|
|
59
|
+
closeOnOutsideClick: boolean = true;
|
|
60
|
+
|
|
61
|
+
@queryAsync("dialog")
|
|
62
|
+
dialogElement!: Promise<HTMLDialogElement>;
|
|
63
|
+
|
|
64
|
+
connectedCallback(): void {
|
|
65
|
+
super.connectedCallback();
|
|
66
|
+
this.dialogElement.then((dialog) =>
|
|
67
|
+
dialog.addEventListener("keydown", this._handleKeyDown)
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
disconnectedCallback(): void {
|
|
72
|
+
super.disconnectedCallback();
|
|
73
|
+
this.dialogElement.then((dialog) =>
|
|
74
|
+
dialog.removeEventListener("keydown", this._handleKeyDown)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected updated(_changedProperties: PropertyValues): void {
|
|
79
|
+
if (_changedProperties.has("open")) {
|
|
80
|
+
this.dialogElement.then((dialog) => {
|
|
81
|
+
if (this.open) {
|
|
82
|
+
dialog.showModal();
|
|
83
|
+
} else {
|
|
84
|
+
dialog.close();
|
|
34
85
|
}
|
|
35
|
-
|
|
36
|
-
render(body, frag);
|
|
37
|
-
dialog.appendChild(frag)
|
|
38
|
-
dialog.open = true;
|
|
39
|
-
document.body.appendChild(dialog)
|
|
40
|
-
return dialog
|
|
86
|
+
});
|
|
41
87
|
}
|
|
42
|
-
|
|
43
|
-
open: boolean = false;
|
|
44
|
-
|
|
45
|
-
@property({ type: Boolean, reflect: true })
|
|
46
|
-
dismissable: boolean = true;
|
|
47
|
-
|
|
48
|
-
@property({ type: Boolean, reflect: true })
|
|
49
|
-
hideBackdrop: boolean = false;
|
|
50
|
-
|
|
51
|
-
@property({ type: String, reflect: true })
|
|
52
|
-
title: string = "";
|
|
88
|
+
}
|
|
53
89
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
closeOnOutsideClick: boolean = true;
|
|
59
|
-
|
|
60
|
-
@queryAsync("dialog")
|
|
61
|
-
dialogElement!: Promise<HTMLDialogElement>;
|
|
62
|
-
|
|
63
|
-
connectedCallback(): void {
|
|
64
|
-
super.connectedCallback();
|
|
65
|
-
this.dialogElement.then(dialog => dialog.addEventListener('keydown', this._handleKeyDown));
|
|
90
|
+
private clickHandler = async (e: PointerEvent): Promise<void> => {
|
|
91
|
+
const dialog = await this.dialogElement;
|
|
92
|
+
if (!this.dismissable || !this.closeOnOutsideClick) {
|
|
93
|
+
return;
|
|
66
94
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this.dialogElement.then(dialog => dialog.removeEventListener('keydown', this._handleKeyDown));
|
|
95
|
+
if (dialog === e.target) {
|
|
96
|
+
// clicked on backdrop
|
|
97
|
+
this.open = false;
|
|
71
98
|
}
|
|
99
|
+
};
|
|
72
100
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (this.open) {
|
|
77
|
-
dialog.showModal();
|
|
78
|
-
} else {
|
|
79
|
-
dialog.close();
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
}
|
|
101
|
+
private _handleKeyDown = (e: KeyboardEvent): void => {
|
|
102
|
+
if (!this.closeOnEscape) {
|
|
103
|
+
e.preventDefault();
|
|
83
104
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const dialog = await this.dialogElement;
|
|
87
|
-
if (!this.dismissable || !this.closeOnOutsideClick) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (dialog === e.target) {
|
|
91
|
-
// clicked on backdrop
|
|
92
|
-
this.open = false;
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
private _handleKeyDown = (e: KeyboardEvent): void => {
|
|
97
|
-
if (!this.closeOnEscape) {
|
|
98
|
-
e.preventDefault()
|
|
99
|
-
}
|
|
100
|
-
if (this.open && this.closeOnEscape && e.key === 'Escape') {
|
|
101
|
-
this.open = false;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
private _onClose = (e: PointerEvent): void => {
|
|
106
|
-
e.preventDefault();
|
|
107
|
-
e.stopPropagation();
|
|
108
|
-
this.open = false;
|
|
109
|
-
this.dispatchEvent(new CustomEvent('close', {
|
|
110
|
-
bubbles: true,
|
|
111
|
-
composed: true,
|
|
112
|
-
}));
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
protected render(): unknown {
|
|
116
|
-
return html`
|
|
117
|
-
<dialog class=${this.hideBackdrop ? '' : 'backdrop'} @close=${this._onClose} @click=${this.clickHandler}>
|
|
118
|
-
<spectric-panel>
|
|
119
|
-
<div class="modal">
|
|
120
|
-
${this.dismissable ? html`<spectric-button variant="text" size="small" @click=${() => { this.open = false }}>X</spectric-button>` : null}
|
|
121
|
-
<h3 >
|
|
122
|
-
<slot name="title">
|
|
123
|
-
${this.title.length ? this.title : null}
|
|
124
|
-
</slot>
|
|
125
|
-
</h3>
|
|
126
|
-
|
|
127
|
-
<div class="content"><slot></slot></div>
|
|
128
|
-
<div class="footer"><slot name="footer"></slot></div>
|
|
129
|
-
</div>
|
|
130
|
-
</spectric-panel>
|
|
131
|
-
</dialog>
|
|
132
|
-
`;
|
|
105
|
+
if (this.open && this.closeOnEscape && e.key === "Escape") {
|
|
106
|
+
this.open = false;
|
|
133
107
|
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
private _onClose = (e: PointerEvent): void => {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
e.stopPropagation();
|
|
113
|
+
this.open = false;
|
|
114
|
+
this.dispatchEvent(
|
|
115
|
+
new CustomEvent("close", {
|
|
116
|
+
bubbles: true,
|
|
117
|
+
composed: true,
|
|
118
|
+
})
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
protected render(): unknown {
|
|
123
|
+
return html`
|
|
124
|
+
<dialog
|
|
125
|
+
class=${this.hideBackdrop ? "" : "backdrop"}
|
|
126
|
+
@close=${this._onClose}
|
|
127
|
+
@click=${this.clickHandler}
|
|
128
|
+
>
|
|
129
|
+
<spectric-panel>
|
|
130
|
+
<div class="modal">
|
|
131
|
+
${this.dismissable
|
|
132
|
+
? html`<spectric-button
|
|
133
|
+
variant="text"
|
|
134
|
+
size="small"
|
|
135
|
+
@click=${() => {
|
|
136
|
+
this.open = false;
|
|
137
|
+
}}
|
|
138
|
+
icon
|
|
139
|
+
>X</spectric-button
|
|
140
|
+
>`
|
|
141
|
+
: null}
|
|
142
|
+
<h3>
|
|
143
|
+
<slot name="title">
|
|
144
|
+
${this.title.length ? this.title : null}
|
|
145
|
+
</slot>
|
|
146
|
+
</h3>
|
|
147
|
+
|
|
148
|
+
<div class="content"><slot></slot></div>
|
|
149
|
+
<div class="footer"><slot name="footer"></slot></div>
|
|
150
|
+
</div>
|
|
151
|
+
</spectric-panel>
|
|
152
|
+
</dialog>
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
134
155
|
}
|
|
135
156
|
|
|
136
157
|
interface DialogEvents {
|
|
137
|
-
|
|
158
|
+
close: (event: CustomEvent) => void;
|
|
138
159
|
}
|
|
139
160
|
|
|
140
161
|
declare global {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
162
|
+
interface HTMLElementTagNameMap {
|
|
163
|
+
[DialogElementTag]: HTMLElementTagWithEvents<DialogElement, DialogEvents>;
|
|
164
|
+
}
|
|
165
|
+
namespace JSX {
|
|
166
|
+
interface IntrinsicElements {
|
|
167
|
+
/**
|
|
168
|
+
* @see {@link DialogElement}
|
|
169
|
+
*/
|
|
170
|
+
[DialogElementTag]: ReactElementWithPropsAndEvents<
|
|
171
|
+
DialogElement,
|
|
172
|
+
DialogProps,
|
|
173
|
+
DialogEvents
|
|
174
|
+
>;
|
|
144
175
|
}
|
|
176
|
+
}
|
|
177
|
+
namespace React {
|
|
145
178
|
namespace JSX {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* @see {@link DialogElement}
|
|
158
|
-
*/
|
|
159
|
-
[DialogElementTag]: ReactElementWithPropsAndEvents<DialogElement, DialogProps, DialogEvents>
|
|
160
|
-
}
|
|
161
|
-
}
|
|
179
|
+
interface IntrinsicElements {
|
|
180
|
+
/**
|
|
181
|
+
* @see {@link DialogElement}
|
|
182
|
+
*/
|
|
183
|
+
[DialogElementTag]: ReactElementWithPropsAndEvents<
|
|
184
|
+
DialogElement,
|
|
185
|
+
DialogProps,
|
|
186
|
+
DialogEvents
|
|
187
|
+
>;
|
|
188
|
+
}
|
|
162
189
|
}
|
|
190
|
+
}
|
|
163
191
|
}
|
|
@@ -11,7 +11,7 @@ import { SpectricButton } from "../Button";
|
|
|
11
11
|
import { PopoverElement } from "../tooltip/popover";
|
|
12
12
|
export type FieldTypes = {
|
|
13
13
|
name: string;
|
|
14
|
-
type: "string" | "number" | "boolean"
|
|
14
|
+
type: "string" | "number" | "boolean" | "integer" | "object"
|
|
15
15
|
format?: "date-time"
|
|
16
16
|
}
|
|
17
17
|
type SuggestionType = 'conjunction' | "field" | 'operator' | 'value'
|
|
@@ -76,6 +76,9 @@ const NumberOperators: Record<string, LabelValue> = {
|
|
|
76
76
|
"gte": { value: " >= ", label: " is greater than or equal to some value" },
|
|
77
77
|
"lte": { value: " <= ", label: " is less than or equal to some value" }
|
|
78
78
|
}
|
|
79
|
+
const ObjectOperators: Record<string, LabelValue> = {
|
|
80
|
+
"exists": { value: ": *", label: " exists in any form" }
|
|
81
|
+
}
|
|
79
82
|
const StringOperators: Record<string, LabelValue> = {
|
|
80
83
|
"eq": { value: ": ", label: " equals some value" },
|
|
81
84
|
"exists": { value: ": *", label: " exists in any form" }
|
|
@@ -199,8 +202,10 @@ export class SpectricQuery extends LitElement implements IQueryProps {
|
|
|
199
202
|
if (type === "operator") {
|
|
200
203
|
let fieldType = this.fields.find(field => field.name === suggestion.fieldName);
|
|
201
204
|
if (fieldType) {
|
|
202
|
-
if (fieldType.type
|
|
205
|
+
if (["number", "integer"].includes(fieldType.type)) {
|
|
203
206
|
completions.push(...Object.values(NumberOperators).map(value => ({ type, ...value, start: end, end: end })))
|
|
207
|
+
} else if (fieldType.type === "object") {
|
|
208
|
+
completions.push(...Object.values(ObjectOperators).map(value => ({ type, ...value, start: end, end: end })))
|
|
204
209
|
} else if (fieldType.type === "string") {
|
|
205
210
|
if (fieldType.format === "date-time") {
|
|
206
211
|
completions.push(...Object.values(DateOperators).map(value => ({
|