@spectric/ui 0.0.4
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/.gitlab-ci.yml +28 -0
- package/.nvmrc +1 -0
- package/.storybook/analyze.sh +4 -0
- package/.storybook/main.ts +55 -0
- package/.storybook/preview.ts +42 -0
- package/.vscode/extensions.json +5 -0
- package/.vscode/settings.json +41 -0
- package/README.MD +50 -0
- package/html-include.png +0 -0
- package/package.json +33 -0
- package/src/classes/BitArray.ts +48 -0
- package/src/classes/DisposibleElement.ts +108 -0
- package/src/components/Banner.ts +102 -0
- package/src/components/Bitdisplay.ts +383 -0
- package/src/components/Button.ts +121 -0
- package/src/components/Header.ts +125 -0
- package/src/components/Page.ts +157 -0
- package/src/components/Panel.ts +56 -0
- package/src/components/ThemeProvider.ts +251 -0
- package/src/components/button.css.ts +160 -0
- package/src/components/configurations/classifications.ts +194 -0
- package/src/components/dialog/dialog.css.ts +50 -0
- package/src/components/dialog/dialog.ts +163 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/header.css.ts +38 -0
- package/src/components/index.ts +10 -0
- package/src/components/input.css +75 -0
- package/src/components/input.ts +312 -0
- package/src/components/page.css.ts +158 -0
- package/src/components/panel.css.ts +44 -0
- package/src/components/query_bar/QueryBar.css +48 -0
- package/src/components/query_bar/QueryBar.ts +378 -0
- package/src/components/query_bar/index.ts +2 -0
- package/src/components/query_bar/querylanguage/kuery/ast/_generated_/kuery.js +3186 -0
- package/src/components/query_bar/querylanguage/kuery/ast/ast.ts +113 -0
- package/src/components/query_bar/querylanguage/kuery/ast/index.ts +31 -0
- package/src/components/query_bar/querylanguage/kuery/ast/kuery.peg +417 -0
- package/src/components/query_bar/querylanguage/kuery/functions/and.ts +55 -0
- package/src/components/query_bar/querylanguage/kuery/functions/exists.ts +62 -0
- package/src/components/query_bar/querylanguage/kuery/functions/index.ts +47 -0
- package/src/components/query_bar/querylanguage/kuery/functions/is.ts +211 -0
- package/src/components/query_bar/querylanguage/kuery/functions/nested.ts +63 -0
- package/src/components/query_bar/querylanguage/kuery/functions/not.ts +53 -0
- package/src/components/query_bar/querylanguage/kuery/functions/or.ts +56 -0
- package/src/components/query_bar/querylanguage/kuery/functions/range.ts +163 -0
- package/src/components/query_bar/querylanguage/kuery/functions/utils/get_fields.ts +49 -0
- package/src/components/query_bar/querylanguage/kuery/functions/utils/get_full_field_name_node.ts +87 -0
- package/src/components/query_bar/querylanguage/kuery/index.ts +38 -0
- package/src/components/query_bar/querylanguage/kuery/kuery_syntax_error.ts +76 -0
- package/src/components/query_bar/querylanguage/kuery/node_types/function.ts +75 -0
- package/src/components/query_bar/querylanguage/kuery/node_types/index.ts +46 -0
- package/src/components/query_bar/querylanguage/kuery/node_types/literal.ts +42 -0
- package/src/components/query_bar/querylanguage/kuery/node_types/named_arg.ts +47 -0
- package/src/components/query_bar/querylanguage/kuery/node_types/types.ts +108 -0
- package/src/components/query_bar/querylanguage/kuery/node_types/wildcard.ts +80 -0
- package/src/components/query_bar/querylanguage/kuery/types.ts +52 -0
- package/src/components/query_bar/querylanguage/outputTypes/toCQL.ts +122 -0
- package/src/components/query_bar/querylanguage/outputTypes/toMongo.ts +103 -0
- package/src/components/query_bar/querylanguage/utils.ts +35 -0
- package/src/components/query_bar/types.ts +59 -0
- package/src/components/splitview/index.ts +1 -0
- package/src/components/splitview/splitview.css.ts +66 -0
- package/src/components/splitview/splitview.ts +183 -0
- package/src/components/types.ts +35 -0
- package/src/index.ts +1 -0
- package/src/stories/Banner.stories.ts +46 -0
- package/src/stories/BitDisplay.stories.ts +68 -0
- package/src/stories/Button.stories.ts +138 -0
- package/src/stories/Header.stories.ts +55 -0
- package/src/stories/Page.stories.ts +108 -0
- package/src/stories/QueryBar.stories.ts +63 -0
- package/src/stories/Splitview.stories.ts +52 -0
- package/src/stories/fixtures/Bits.ts +15 -0
- package/src/stories/fixtures/ExampleContent.ts +102 -0
- package/src/stories/fixtures/data.ts +30 -0
- package/src/stories/fixtures/lorumipsum.ts +19 -0
- package/src/stories/input.stories.ts +77 -0
- package/src/stories/tsconfig.json +35 -0
- package/src/utils/debounce.ts +18 -0
- package/src/utils/spread.ts +71 -0
- package/src/vite-env.d.ts +1 -0
- package/test/__init__.py +9 -0
- package/test/elastic.py +9 -0
- package/test/interface.py +16 -0
- package/tsconfig.json +29 -0
- package/vite.config.js +34 -0
- package/vue-example.png +0 -0
- package/vue-include.png +0 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { css, html, PropertyValues } from 'lit';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { customElement, property, queryAsync } from 'lit/decorators.js';
|
|
5
|
+
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
|
6
|
+
import { BitArray } from "../classes/BitArray"
|
|
7
|
+
import { DisposableElement, IDisposable } from '../classes/DisposibleElement';
|
|
8
|
+
import { debounceAnimation } from '../utils/debounce';
|
|
9
|
+
import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from './types';
|
|
10
|
+
|
|
11
|
+
export interface BitDisplayProps {
|
|
12
|
+
/** Array buffer to display */
|
|
13
|
+
arrayBuffer: ArrayBuffer;
|
|
14
|
+
/** Bits per line */
|
|
15
|
+
frameWidth: number;
|
|
16
|
+
/** How many pixels per bit */
|
|
17
|
+
scale: number;
|
|
18
|
+
/** Width of the display canvas */
|
|
19
|
+
width: number;
|
|
20
|
+
/** Height of the display canvas */
|
|
21
|
+
height: number;
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
type Position = {
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
}
|
|
28
|
+
export type BitDisplayMouseEvent = {
|
|
29
|
+
position: Position,
|
|
30
|
+
nativeEvent: MouseEvent
|
|
31
|
+
bitIndex: number
|
|
32
|
+
}
|
|
33
|
+
const events = ["mousemove", "click", "dblclick", "mousedown", "mouseup", "contextmenu"] as const
|
|
34
|
+
type BitEventHandler = (event: CustomEvent<BitDisplayMouseEvent>) => void
|
|
35
|
+
type AllEvents = typeof events
|
|
36
|
+
|
|
37
|
+
export interface BitDisplayEventMap extends Record<`bit${Capitalize<AllEvents[number]>}`, BitEventHandler> {
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const DEFAULT_SIZE = { width: 200, height: 200 }
|
|
42
|
+
const SCROLLBAR_SIZE = { width: 10, height: 10 }
|
|
43
|
+
/**
|
|
44
|
+
* @fires bitMousemove - emits BitDisplayMouseEvent on mousemove over the canvas
|
|
45
|
+
* @fires bitClick - emits BitDisplayMouseEvent on mouse click on the canvas
|
|
46
|
+
* @fires bitDblclick - emits BitDisplayMouseEvent on mouse double click on the canvas
|
|
47
|
+
* @fires bitMousedown - emits BitDisplayMouseEvent on mouse down onthe canvas
|
|
48
|
+
* @fires bitMouseup - emits BitDisplayMouseEvent on mouse up on the canvas
|
|
49
|
+
* @fires bitContextmenu - emits BitDisplayMouseEvent on mouse right click on the canvas
|
|
50
|
+
*/
|
|
51
|
+
@customElement("spectric-bit-display")
|
|
52
|
+
export class BitDisplayCanvas extends DisposableElement implements BitDisplayProps {
|
|
53
|
+
@property({ type: Object, attribute: false })
|
|
54
|
+
arrayBuffer: ArrayBuffer = new ArrayBuffer(0);
|
|
55
|
+
@property({ type: Number })
|
|
56
|
+
frameWidth: number = 100;
|
|
57
|
+
@property({ type: Number })
|
|
58
|
+
scale: number = 1;
|
|
59
|
+
@property({ type: Number })
|
|
60
|
+
width: number = 200;
|
|
61
|
+
@property({ type: Number })
|
|
62
|
+
height: number = 200;
|
|
63
|
+
static styles = css`
|
|
64
|
+
:host{
|
|
65
|
+
position: relative;
|
|
66
|
+
--border-radius: var(--spectric-border-radius,4em);
|
|
67
|
+
--scroll-color: var(--spectric-background-inverse,#393939);
|
|
68
|
+
--background-color: var(--spectric-background, #ffffff);
|
|
69
|
+
}
|
|
70
|
+
#viewport{
|
|
71
|
+
overflow: scroll;
|
|
72
|
+
position: absolute;
|
|
73
|
+
}
|
|
74
|
+
#fakescrolldiv {
|
|
75
|
+
position: relative;
|
|
76
|
+
overflow-x: hidden;
|
|
77
|
+
overflow-y: hidden;
|
|
78
|
+
z-index:-1;
|
|
79
|
+
}
|
|
80
|
+
canvas{
|
|
81
|
+
position: absolute;
|
|
82
|
+
left:0;
|
|
83
|
+
top:0;
|
|
84
|
+
background-color:var(--background-color);
|
|
85
|
+
}
|
|
86
|
+
::-webkit-scrollbar {
|
|
87
|
+
height: ${SCROLLBAR_SIZE.height}px;
|
|
88
|
+
width: ${SCROLLBAR_SIZE.width}px;
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Webkit psudo elements don't pick up variables defined by the host */
|
|
93
|
+
::-webkit-scrollbar-thumb {
|
|
94
|
+
background: var(--spectric-background-inverse,#393939);
|
|
95
|
+
border-radius: var(--spectric-border-radius,4em);
|
|
96
|
+
-webkit-border-radius: var(--spectric-border-radius,4em);
|
|
97
|
+
padding-top: 1px;
|
|
98
|
+
padding-bottom: 1px;
|
|
99
|
+
box-shadow: 0 0 1px var(--spectric-primary,#242424) inset;
|
|
100
|
+
}
|
|
101
|
+
::-webkit-scrollbar-thumb:hover {
|
|
102
|
+
background: var(--spectric-primary,#009eb3);
|
|
103
|
+
}
|
|
104
|
+
::-webkit-scrollbar-corner {
|
|
105
|
+
background: var(--spectric-background, #ffffff);
|
|
106
|
+
}
|
|
107
|
+
/* Buttons */
|
|
108
|
+
|
|
109
|
+
::-webkit-scrollbar-button:single-button {
|
|
110
|
+
background-color: transparent;
|
|
111
|
+
display: block;
|
|
112
|
+
border-style: solid;
|
|
113
|
+
}/* Up */
|
|
114
|
+
::-webkit-scrollbar-button:single-button:vertical:decrement {
|
|
115
|
+
border-width: 0 5px 5px 5px;
|
|
116
|
+
border-color: var(--spectric-background, #ffffff) var(--spectric-background, #ffffff) var(--spectric-background-inverse,#393939) var(--spectric-background, #ffffff);
|
|
117
|
+
}
|
|
118
|
+
::-webkit-scrollbar-button:single-button:vertical:decrement:hover {
|
|
119
|
+
border-color: var(--spectric-background, #ffffff) var(--spectric-background, #ffffff) var(--spectric-primary,#009eb3) var(--spectric-background, #ffffff);
|
|
120
|
+
}
|
|
121
|
+
/* Down */
|
|
122
|
+
::-webkit-scrollbar-button:single-button:vertical:increment {
|
|
123
|
+
border-width: 5px 5px 0 5px;
|
|
124
|
+
border-color: var(--spectric-background-inverse,#393939) var(--spectric-background, #ffffff) transparent var(--spectric-background, #ffffff);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
::-webkit-scrollbar-button:vertical:single-button:increment:hover {
|
|
128
|
+
border-color: var(--spectric-primary,#009eb3) var(--spectric-background, #ffffff) transparent var(--spectric-background, #ffffff);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Left */
|
|
132
|
+
::-webkit-scrollbar-button:single-button:horizontal:decrement {
|
|
133
|
+
border-width: 5px 5px 5px 0;
|
|
134
|
+
border-color:var(--spectric-background, #ffffff) var(--spectric-background-inverse,#393939) var(--spectric-background, #ffffff) var(--spectric-background, #ffffff);
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
::-webkit-scrollbar-button:single-button:horizontal:decrement:hover {
|
|
139
|
+
border-width: 5px 5px 5px 0;
|
|
140
|
+
border-color:var(--spectric-background, #ffffff) var(--spectric-primary,#009eb3) var(--spectric-background, #ffffff) var(--spectric-background, #ffffff);
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
/* Right */
|
|
147
|
+
::-webkit-scrollbar-button:single-button:horizontal:increment {
|
|
148
|
+
border-width: 5px 0 5px 5px;
|
|
149
|
+
border-color:var(--spectric-background, #ffffff) var(--spectric-background, #ffffff) var(--spectric-background, #ffffff) var(--spectric-background-inverse,#393939);
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
::-webkit-scrollbar-button:single-button:horizontal:increment:hover {
|
|
154
|
+
border-width: 5px 0 5px 5px;
|
|
155
|
+
border-color:var(--spectric-background, #ffffff) var(--spectric-background, #ffffff) var(--spectric-background, #ffffff) var(--spectric-primary,#009eb3);
|
|
156
|
+
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
`
|
|
160
|
+
private state: { scale?: number } = {};
|
|
161
|
+
private yStart: number = 0;
|
|
162
|
+
private xStart: number = 0;
|
|
163
|
+
@queryAsync("canvas")
|
|
164
|
+
private _canvas!: Promise<HTMLCanvasElement>
|
|
165
|
+
private refs: {
|
|
166
|
+
viewport: Ref<HTMLDivElement>,
|
|
167
|
+
canvas: Ref<HTMLCanvasElement>, scrollDiv: Ref<HTMLDivElement>
|
|
168
|
+
} = {
|
|
169
|
+
viewport: createRef(),
|
|
170
|
+
canvas: createRef(),
|
|
171
|
+
scrollDiv: createRef()
|
|
172
|
+
};
|
|
173
|
+
private _bitArray?: any;
|
|
174
|
+
|
|
175
|
+
private resizeObserver: ResizeObserver & IDisposable;
|
|
176
|
+
private color: string = "#000000";
|
|
177
|
+
constructor() {
|
|
178
|
+
super();
|
|
179
|
+
let observer = new ResizeObserver(this.onResize) as any
|
|
180
|
+
observer.dispose = () => {
|
|
181
|
+
this.resizeObserver.disconnect()
|
|
182
|
+
}
|
|
183
|
+
this.resizeObserver = observer
|
|
184
|
+
this.addDisposableListener(document.body, "theme-change", this.onThemeChange)
|
|
185
|
+
|
|
186
|
+
var lastPosition: Partial<Record<AllEvents[number], number>> = {}
|
|
187
|
+
events.forEach(eventName => {
|
|
188
|
+
this.addDisposableListener(this._canvas, eventName, debounceAnimation(async (e: MouseEvent) => {
|
|
189
|
+
e.stopImmediatePropagation()
|
|
190
|
+
e.stopPropagation()
|
|
191
|
+
e.preventDefault()
|
|
192
|
+
var canvas = await (this._canvas)
|
|
193
|
+
let rect = canvas.getBoundingClientRect()
|
|
194
|
+
let position = {
|
|
195
|
+
x: Math.floor(Math.abs(e.clientX - rect.left)),
|
|
196
|
+
y: Math.floor(Math.abs(e.clientY - rect.top))
|
|
197
|
+
};
|
|
198
|
+
let bitIndex = this.canvasPositionToBit(position)
|
|
199
|
+
//If the bit index didn't change we don't need to fire an event, this can happen when the scale is more than 1px
|
|
200
|
+
if (eventName === "mousemove" && lastPosition[eventName] == bitIndex) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
lastPosition[eventName] = bitIndex
|
|
204
|
+
let bitEventName = "bit" + String(eventName).charAt(0).toUpperCase() + String(eventName).slice(1)
|
|
205
|
+
if (bitIndex != -1) {
|
|
206
|
+
let event = new CustomEvent<BitDisplayMouseEvent>(bitEventName, {
|
|
207
|
+
detail: { bitIndex, position, nativeEvent: e }
|
|
208
|
+
})
|
|
209
|
+
this.dispatchEvent(event)
|
|
210
|
+
}
|
|
211
|
+
}))
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
}
|
|
215
|
+
private onThemeChange = () => {
|
|
216
|
+
let cssStyle = getComputedStyle(this);
|
|
217
|
+
if (this.color != cssStyle.color) {
|
|
218
|
+
this.color = cssStyle.color
|
|
219
|
+
this.paint()
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
connectedCallback(): void {
|
|
223
|
+
this.onResize()
|
|
224
|
+
super.connectedCallback()
|
|
225
|
+
this.registerDisposable(this.resizeObserver)
|
|
226
|
+
this.resizeObserver.observe(this)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
protected updated(changedProperties: PropertyValues): void {
|
|
231
|
+
if (changedProperties.has("arrayBuffer")) {
|
|
232
|
+
if (this.arrayBuffer) {
|
|
233
|
+
this._bitArray = new BitArray(this.arrayBuffer);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const repaintProps = ["arrayBuffer", "scale", "frameWidth", "width", "height"]
|
|
237
|
+
for (const prop of repaintProps) {
|
|
238
|
+
if (changedProperties.has(prop)) {
|
|
239
|
+
this.paint();
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
private onResize = () => {
|
|
247
|
+
this.calculateViewport();
|
|
248
|
+
}
|
|
249
|
+
canvasPositionToBit(position: { x: number, y: number }) {
|
|
250
|
+
let x = Math.floor(position.x / this.scale)
|
|
251
|
+
let y = Math.floor(position.y / this.scale)
|
|
252
|
+
if (x > this.frameWidth - 1) {
|
|
253
|
+
return -1
|
|
254
|
+
}
|
|
255
|
+
let startBit = this.yStart * this.frameWidth + this.xStart
|
|
256
|
+
return startBit + (y * this.frameWidth + x)
|
|
257
|
+
}
|
|
258
|
+
paint() {
|
|
259
|
+
if (!this.refs.canvas.value || !this._bitArray) {
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
const { frameWidth, _bitArray: bitArray } = this;
|
|
263
|
+
if (frameWidth <= 0) {
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
this.calculateViewport()
|
|
267
|
+
const { width = DEFAULT_SIZE.width, height = DEFAULT_SIZE.height } = this;
|
|
268
|
+
const scale = this.getPxPerBit()
|
|
269
|
+
const context = this.refs.canvas.value.getContext("2d");
|
|
270
|
+
if (!context) {
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
if (this.state.scale != scale) {
|
|
274
|
+
context.reset()
|
|
275
|
+
context.scale(scale, scale);
|
|
276
|
+
}
|
|
277
|
+
let cssStyle = getComputedStyle(this);
|
|
278
|
+
context.fillStyle = cssStyle.color
|
|
279
|
+
context.clearRect(0, 0, width, height);
|
|
280
|
+
let y = 0;
|
|
281
|
+
|
|
282
|
+
for (
|
|
283
|
+
let i = this.yStart * frameWidth + this.xStart, len = bitArray.length;
|
|
284
|
+
i < len;
|
|
285
|
+
i++
|
|
286
|
+
) {
|
|
287
|
+
let x = (i % frameWidth) - this.xStart;
|
|
288
|
+
if (x < 0) {
|
|
289
|
+
continue; //Less than 0 we are off screen
|
|
290
|
+
}
|
|
291
|
+
if (Math.floor(x * scale) > width) {
|
|
292
|
+
y++;
|
|
293
|
+
i = y * frameWidth + this.yStart * frameWidth + this.xStart - 1;
|
|
294
|
+
continue; //Don't bother going if it is off screen
|
|
295
|
+
}
|
|
296
|
+
if (Math.floor(y * scale) > height) {
|
|
297
|
+
break; //Don't bother going if it is off screen
|
|
298
|
+
}
|
|
299
|
+
if (bitArray.getBit(i) === 1) {
|
|
300
|
+
context.fillRect(x, y, 1, 1);
|
|
301
|
+
}
|
|
302
|
+
if ((i + 1) % frameWidth === 0 && i >= frameWidth - 1) {
|
|
303
|
+
y++;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
private handleScroll = () => {
|
|
308
|
+
if (!this.refs.viewport.value) {
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
const scale = this.getPxPerBit()
|
|
312
|
+
this.yStart = this.refs.viewport.value.scrollTop
|
|
313
|
+
? Math.floor(this.refs.viewport.value.scrollTop / scale) + 1
|
|
314
|
+
: 0;
|
|
315
|
+
this.xStart = this.refs.viewport.value.scrollLeft
|
|
316
|
+
? Math.floor(this.refs.viewport.value.scrollLeft / scale) + 1
|
|
317
|
+
: 0;
|
|
318
|
+
this.paint();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
getPxPerBit() {
|
|
322
|
+
return this.scale > 0 ? this.scale : 1;
|
|
323
|
+
}
|
|
324
|
+
calculateViewport() {
|
|
325
|
+
const { frameWidth, _bitArray: bitArray } = this;
|
|
326
|
+
if (this.refs.scrollDiv.value) {
|
|
327
|
+
var pxPerBit = this.getPxPerBit();
|
|
328
|
+
const height = (bitArray.length / frameWidth) * pxPerBit;
|
|
329
|
+
const width = frameWidth * pxPerBit;
|
|
330
|
+
const div = this.refs.scrollDiv.value;
|
|
331
|
+
div.style.width = width + "px";
|
|
332
|
+
div.style.height = height + "px"
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
protected render(): unknown {
|
|
336
|
+
//return html`<div>here</div>`
|
|
337
|
+
const { height = DEFAULT_SIZE.height, width = DEFAULT_SIZE.width } = this
|
|
338
|
+
return html`
|
|
339
|
+
<div
|
|
340
|
+
id="viewport"
|
|
341
|
+
${ref(this.refs.viewport)}
|
|
342
|
+
@scroll=${this.handleScroll}
|
|
343
|
+
style=${`width:${width}px;height:${height}px;overflow:auto;position:relative`}
|
|
344
|
+
>
|
|
345
|
+
<div
|
|
346
|
+
id="fakescrolldiv"
|
|
347
|
+
${ref(this.refs.scrollDiv)}
|
|
348
|
+
>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
<canvas
|
|
352
|
+
${ref(this.refs.canvas)}
|
|
353
|
+
width=${width - SCROLLBAR_SIZE.width}
|
|
354
|
+
height=${height - SCROLLBAR_SIZE.height}
|
|
355
|
+
></canvas>
|
|
356
|
+
`
|
|
357
|
+
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
declare global {
|
|
362
|
+
interface HTMLElementTagNameMap {
|
|
363
|
+
"spectric-bit-display": HTMLElementTagWithEvents<BitDisplayCanvas, BitDisplayEventMap>
|
|
364
|
+
}
|
|
365
|
+
namespace JSX {
|
|
366
|
+
interface IntrinsicElements {
|
|
367
|
+
/**
|
|
368
|
+
* {@link BitDisplayCanvas}
|
|
369
|
+
*/
|
|
370
|
+
"spectric-bit-display": ReactElementWithPropsAndEvents<BitDisplayCanvas, BitDisplayProps, BitDisplayEventMap>;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
namespace React {
|
|
374
|
+
namespace JSX {
|
|
375
|
+
interface IntrinsicElements {
|
|
376
|
+
/**
|
|
377
|
+
* {@link BitDisplayCanvas}
|
|
378
|
+
*/
|
|
379
|
+
"spectric-bit-display": ReactElementWithPropsAndEvents<BitDisplayCanvas, BitDisplayProps, BitDisplayEventMap>;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { CSSResultGroup, html, LitElement } from 'lit';
|
|
2
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
3
|
+
|
|
4
|
+
import style from './button.css';
|
|
5
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
6
|
+
import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from './types';
|
|
7
|
+
|
|
8
|
+
export enum ButtonSizes {
|
|
9
|
+
large = "large",
|
|
10
|
+
medium = "medium",
|
|
11
|
+
small = "small",
|
|
12
|
+
}
|
|
13
|
+
type ButtonSizesTypes = `${ButtonSizes}`
|
|
14
|
+
export enum ButtonVariants {
|
|
15
|
+
primary = "primary",
|
|
16
|
+
secondary = "secondary",
|
|
17
|
+
text = "text"
|
|
18
|
+
}
|
|
19
|
+
type ButtonVariantsTypes = `${ButtonVariants}`
|
|
20
|
+
export interface ButtonProps {
|
|
21
|
+
/** Is this the principal call to action on the page? */
|
|
22
|
+
variant: ButtonVariantsTypes;
|
|
23
|
+
/** What background color to use */
|
|
24
|
+
backgroundColor?: string;
|
|
25
|
+
/** How large should the button be? */
|
|
26
|
+
size: ButtonSizesTypes;
|
|
27
|
+
label?: string,
|
|
28
|
+
/* should the button be disabled*/
|
|
29
|
+
disabled: boolean;
|
|
30
|
+
danger?: boolean;
|
|
31
|
+
}
|
|
32
|
+
const MODES = {
|
|
33
|
+
"primary": 'spectric-button--primary',
|
|
34
|
+
"secondary": 'spectric-button--secondary',
|
|
35
|
+
"text": 'spectric-button--text',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@customElement('spectric-button')
|
|
39
|
+
export class SpectricButton extends LitElement implements ButtonProps {
|
|
40
|
+
|
|
41
|
+
static styles?: CSSResultGroup | undefined = style;
|
|
42
|
+
@property({ type: String, reflect: true })
|
|
43
|
+
variant: 'primary' | 'secondary' | 'text' = "primary";
|
|
44
|
+
@property({ type: Boolean, reflect: true })
|
|
45
|
+
disabled: boolean = false;
|
|
46
|
+
@property({ type: String, reflect: true })
|
|
47
|
+
backgroundColor?: string | undefined;
|
|
48
|
+
@property({ type: String, reflect: true })
|
|
49
|
+
size: 'small' | 'medium' | 'large' = "large";
|
|
50
|
+
@property({ type: String, reflect: true })
|
|
51
|
+
label?: string = "";
|
|
52
|
+
@property({ type: Boolean, reflect: true })
|
|
53
|
+
danger = false;
|
|
54
|
+
connectedCallback(): void {
|
|
55
|
+
super.connectedCallback()
|
|
56
|
+
this.addEventListener("click", this._onClick)
|
|
57
|
+
}
|
|
58
|
+
disconnectedCallback(): void {
|
|
59
|
+
super.disconnectedCallback()
|
|
60
|
+
this.removeEventListener("click", this._onClick)
|
|
61
|
+
}
|
|
62
|
+
private _onClick = (e: MouseEvent) => {
|
|
63
|
+
if (e instanceof CustomEvent) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
e.preventDefault()
|
|
67
|
+
e.stopImmediatePropagation()
|
|
68
|
+
e.stopPropagation()
|
|
69
|
+
const options: CustomEventInit = {
|
|
70
|
+
bubbles: true,
|
|
71
|
+
composed: true,
|
|
72
|
+
detail: e
|
|
73
|
+
};
|
|
74
|
+
this.dispatchEvent(new CustomEvent<MouseEvent>('click', options));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
protected render(): unknown {
|
|
78
|
+
const mode = MODES[this.variant] || 'spectric-button--primary';
|
|
79
|
+
return html`
|
|
80
|
+
<button
|
|
81
|
+
type="button"
|
|
82
|
+
?disabled=${this.disabled}
|
|
83
|
+
class=${['spectric-button', `spectric-button-danger--${this.danger || false}`, `spectric-button--${this.size || 'medium'}`, mode].join(' ')}
|
|
84
|
+
style=${styleMap({ backgroundColor: this.backgroundColor })}
|
|
85
|
+
>
|
|
86
|
+
<span class="animation"></span>
|
|
87
|
+
<slot>${this.label}</slot>
|
|
88
|
+
</button>
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface ButtonEventMap {
|
|
94
|
+
//"aaa":(event:CustomEvent<ButtonProps>)=>void
|
|
95
|
+
"click": (event: CustomEvent<MouseEvent>) => void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
declare global {
|
|
99
|
+
interface HTMLElementTagNameMap {
|
|
100
|
+
"spectric-button": HTMLElementTagWithEvents<SpectricButton, ButtonEventMap>
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
namespace JSX {
|
|
104
|
+
interface IntrinsicElements {
|
|
105
|
+
/**
|
|
106
|
+
* {@link SpectricButton}
|
|
107
|
+
*/
|
|
108
|
+
"spectric-button": ReactElementWithPropsAndEvents<SpectricButton, ButtonProps, ButtonEventMap>;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
namespace React {
|
|
112
|
+
namespace JSX {
|
|
113
|
+
interface IntrinsicElements {
|
|
114
|
+
/**
|
|
115
|
+
* {@link SpectricButton}
|
|
116
|
+
*/
|
|
117
|
+
"spectric-button": ReactElementWithPropsAndEvents<SpectricButton, ButtonProps, ButtonEventMap>;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { html, LitElement, TemplateResult } from 'lit';
|
|
2
|
+
|
|
3
|
+
import '.';
|
|
4
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
5
|
+
import styles from './header.css';
|
|
6
|
+
import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from './types';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Properties defining
|
|
11
|
+
*/
|
|
12
|
+
export interface HeaderProps {
|
|
13
|
+
username: string;
|
|
14
|
+
/**
|
|
15
|
+
* Shows the login Button
|
|
16
|
+
*/
|
|
17
|
+
showLoginButton?: boolean
|
|
18
|
+
/**
|
|
19
|
+
* Shows the create account Button when not logged in
|
|
20
|
+
*/
|
|
21
|
+
showCreateAccount?: boolean
|
|
22
|
+
}
|
|
23
|
+
export interface HeaderSlots {
|
|
24
|
+
/** Main name of the application */
|
|
25
|
+
name?: string | Node | TemplateResult
|
|
26
|
+
/** Slot to the left of the title typically used for app icons, and/or dropdown navigation menus.*/
|
|
27
|
+
branding?: string | Node | TemplateResult
|
|
28
|
+
/** Slot in the center of the header.*/
|
|
29
|
+
center?: string | Node | TemplateResult
|
|
30
|
+
/** slot right of login/out buttons*/
|
|
31
|
+
end?: string | Node | TemplateResult
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface HeaderEventMap {
|
|
35
|
+
"login": (event: CustomEvent<void>) => void;
|
|
36
|
+
"logout": (event: CustomEvent<void>) => void;
|
|
37
|
+
"createAccount": (event: CustomEvent<void>) => void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @slot name - Main name of the application
|
|
42
|
+
* @slot branding - Slot to the left of the title typically used for app icons, and/or dropdown navigation menus.
|
|
43
|
+
* @slot center - Slot in the center of the header.
|
|
44
|
+
* @slot end - slot right of login/out buttons
|
|
45
|
+
*/
|
|
46
|
+
@customElement("spectric-header")
|
|
47
|
+
export class Header extends LitElement implements HeaderProps {
|
|
48
|
+
static styles = styles
|
|
49
|
+
@property({ type: Boolean, reflect: true }) showCreateAccount: boolean = false;
|
|
50
|
+
@property({ type: Boolean, reflect: true }) showLoginButton: boolean = false;
|
|
51
|
+
@property({ type: String, reflect: true }) username: string = "";
|
|
52
|
+
onLogout = () => {
|
|
53
|
+
const options = {
|
|
54
|
+
bubbles: true,
|
|
55
|
+
composed: true,
|
|
56
|
+
};
|
|
57
|
+
this.dispatchEvent(new CustomEvent('logout', options));
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
onLogin = () => {
|
|
61
|
+
const options = {
|
|
62
|
+
bubbles: true,
|
|
63
|
+
composed: true,
|
|
64
|
+
};
|
|
65
|
+
this.dispatchEvent(new CustomEvent('login', options));
|
|
66
|
+
}
|
|
67
|
+
onCreateAccount = () => {
|
|
68
|
+
const options = {
|
|
69
|
+
bubbles: true,
|
|
70
|
+
composed: true,
|
|
71
|
+
};
|
|
72
|
+
this.dispatchEvent(new CustomEvent('createAccount', options));
|
|
73
|
+
}
|
|
74
|
+
protected render() {
|
|
75
|
+
return html`
|
|
76
|
+
<header>
|
|
77
|
+
<div class="spectric-header">
|
|
78
|
+
<div style="flex-grow:1;">
|
|
79
|
+
<slot name="branding"></slot>
|
|
80
|
+
<h1><slot name="name"></slot></h1>
|
|
81
|
+
<slot name="center"></slot>
|
|
82
|
+
</div>
|
|
83
|
+
${this.showLoginButton !== false ? html`
|
|
84
|
+
<div>
|
|
85
|
+
${(this.username && this.username !== "")
|
|
86
|
+
? html`<spectric-button variant="secondary" size="small" @click=${this.onLogout}>Log out</spectric-button>`
|
|
87
|
+
: html`
|
|
88
|
+
${this.showCreateAccount !== false ? html`<spectric-button variant="text" size="small" @click=${this.onCreateAccount}>Sign up</spectric-button>` : null}
|
|
89
|
+
<spectric-button variant="primary" size="small" @click=${this.onLogin}>Log in</spectric-button>`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
</div>`: null}
|
|
94
|
+
<slot name="end"></slot>
|
|
95
|
+
</div>
|
|
96
|
+
</header>
|
|
97
|
+
`
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
declare global {
|
|
104
|
+
interface HTMLElementTagNameMap {
|
|
105
|
+
"spectric-header": HTMLElementTagWithEvents<Header, HeaderEventMap>
|
|
106
|
+
}
|
|
107
|
+
namespace JSX {
|
|
108
|
+
interface IntrinsicElements {
|
|
109
|
+
/**
|
|
110
|
+
* {@link Header}
|
|
111
|
+
*/
|
|
112
|
+
"spectric-header": ReactElementWithPropsAndEvents<Header, HeaderProps, HeaderEventMap>;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
namespace React {
|
|
116
|
+
namespace JSX {
|
|
117
|
+
interface IntrinsicElements {
|
|
118
|
+
/**
|
|
119
|
+
* {@link Header}
|
|
120
|
+
*/
|
|
121
|
+
"spectric-header": ReactElementWithPropsAndEvents<Header, HeaderProps, HeaderEventMap>;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|