@spectric/ui 0.0.10 → 0.0.12

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.
Files changed (51) hide show
  1. package/dist/classes/DisposibleElement.d.ts +4 -2
  2. package/dist/components/Bitdisplay.d.ts +4 -4
  3. package/dist/components/Button.d.ts +1 -1
  4. package/dist/components/Header.d.ts +1 -1
  5. package/dist/components/input.d.ts +13 -12
  6. package/dist/components/pagination/pagination.d.ts +5 -5
  7. package/dist/components/splitview/splitview.d.ts +5 -5
  8. package/dist/components/table/body.d.ts +2 -1
  9. package/dist/components/table/cell.d.ts +2 -0
  10. package/dist/components/table/header.d.ts +2 -1
  11. package/dist/components/table/sorting.d.ts +5 -0
  12. package/dist/components/table/table.d.ts +51 -14
  13. package/dist/components/table/virtualBody.d.ts +49 -0
  14. package/dist/components/tooltip/tooltip.d.ts +17 -12
  15. package/dist/custom-elements.json +50 -16
  16. package/dist/index.es.js +2099 -1867
  17. package/dist/index.es.js.map +1 -1
  18. package/dist/index.umd.js +128 -93
  19. package/dist/index.umd.js.map +1 -1
  20. package/dist/style.css +1 -1
  21. package/dist/utils/once.d.ts +1 -0
  22. package/dist/utils/spread.d.ts +1 -1
  23. package/package.json +1 -1
  24. package/src/classes/DisposibleElement.ts +15 -9
  25. package/src/components/Bitdisplay.ts +7 -7
  26. package/src/components/Button.ts +1 -1
  27. package/src/components/Header.ts +1 -1
  28. package/src/components/input.ts +18 -15
  29. package/src/components/pagination/pagination.ts +7 -7
  30. package/src/components/query_bar/QueryBar.ts +26 -21
  31. package/src/components/splitview/splitview.ts +5 -5
  32. package/src/components/table/body.ts +13 -5
  33. package/src/components/table/cell.ts +9 -7
  34. package/src/components/table/header.ts +28 -4
  35. package/src/components/table/sorting.ts +34 -0
  36. package/src/components/table/table.css +60 -4
  37. package/src/components/table/table.ts +149 -33
  38. package/src/components/table/virtualBody.css +13 -0
  39. package/src/components/table/virtualBody.ts +127 -0
  40. package/src/components/tooltip/tooltip.ts +38 -32
  41. package/src/docs/HTML-Vue-Python Integration.mdx +3 -3
  42. package/src/docs/html-include.png +0 -0
  43. package/src/docs/vue-example.png +0 -0
  44. package/src/docs/vue-include.png +0 -0
  45. package/src/stories/BitDisplay.stories.ts +2 -0
  46. package/src/stories/fixtures/ExampleContent.ts +15 -8
  47. package/src/stories/fixtures/data.ts +21 -10
  48. package/src/stories/pagination.stories.ts +2 -1
  49. package/src/stories/table.stories.ts +27 -5
  50. package/src/utils/once.ts +12 -0
  51. package/src/utils/spread.ts +3 -3
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- spectric-input{--input-color: var(--spectric-input-color, #f4f4f4);--border-radius: var(--spectric-border-radius, .4em);--input-bottom: var(--spectric-input-bottom, var(--spectric-button-primary, #a8a8a8));--input-bottom-focused: var(--primary, #1ea7fd);--text-on-color: var(--spectric-text-on-color, #ffffff);--text-on-color-disabled: var(--spectric-text-on-color-disabled, #8d8d8d);--text-placeholder: rgba(22, 22, 22, .4);--text-primary: var(--spectric-text-primary, #161616);--text-secondary: var(--spectric-text-secondary, #525252)}spectric-input .inputWrapper{color:var(--text-secondary)}spectric-input .inputWrapper input{box-sizing:border-box;margin:0;vertical-align:baseline;font-size:.875rem;font-weight:400;line-height:1.28572;letter-spacing:.16px;outline:transparent solid 2px;outline-offset:-2px;border:none;padding:0 1rem;background-color:var(--input-color);color:var(--text-primary, #161616);font-family:inherit;inline-size:100%;block-size:2.5rem}spectric-input .inputWrapper .inputContainer:active:after,spectric-input .inputContainer:focus-within:after{border-bottom-color:var(--input-bottom-focused);width:calc(100% - 5px);transition:width .4s ease-in-out}spectric-input .inputWrapper input:read-only{background-color:transparent;border-bottom-color:var(--border-disabled)}spectric-input .inputContainer{position:relative;border-radius:var(--border-radius);overflow:hidden}spectric-input .inputContainer:after{content:"";width:0px;transition:background-color .4s cubic-bezier(.2,0,.38,.9),border-bottom-color .4s cubic-bezier(.2,0,.38,.9);border-bottom-color:var(--input-bottom);border-bottom-style:solid;border-bottom-width:1px;position:absolute;left:2.5px;bottom:0}spectric-input #helper-text{height:18px}spectric-input[variant=password] spectric-button{position:absolute;right:4px;bottom:3px}spectric-input .checkbox{display:flex;justify-self:center}spectric-query{font-family:monospace}spectric-query .autocomplete{color:var(--spectric-text-primary, #161616);border-radius:0em 0em var(--spectric-border-radius, .4em) var(--spectric-border-radius, .4em);background-color:var(--spectric-background, #ffffff);border:1px solid var(--spectric-background-hover, rgba(141, 141, 141, .12));max-height:300px;border-top:0px;margin:-18px 0 0;position:fixed;top:anchor(bottom);justify-self:anchor-center;text-align:center}spectric-query .autocomplete .optiontype{float:left;max-width:10px}spectric-query .autocomplete .label{position:absolute;right:0}spectric-query .autocomplete .option.active,spectric-query .autocomplete .option:hover{background-color:var(--spectric-background-hover, rgba(141, 141, 141, .12));border-bottom:1px solid var(--primary, #1ea7fd)}spectric-query .autocomplete .option{border-bottom:1px solid transparent;padding:8px}.query-bar-date-quick-select{display:flex;justify-content:space-evenly}spectric-pagination .spectric-pagination-container{display:flex;justify-content:space-between;align-items:center}spectric-pagination .spectric-pagination-text{flex-grow:1;text-align:center}spectric-table{display:flex;flex-direction:column}spectric-table tr{text-align:center}spectric-table-body tr:hover{background-color:color-mix(in srgb,var(--spectric-primary, #1ea7fd),transparent 70%)}spectric-table-header{display:table-header-group;font-weight:700}spectric-table div[role=table]{display:table}spectric-table-body{display:table-row-group}spectric-table-cell{display:contents;vertical-align:middle}spectric-table-cell td{position:relative}spectric-table td:hover:has(.filterable){border:1px solid var(--spectric-primary, #1ea7fd)}spectric-table td{border:1px solid transparent}spectric-table-cell .table-cell-actions{position:absolute;display:flex;width:100%;flex-direction:row-reverse;visibility:hidden;top:-10px}spectric-table-cell td:hover .table-cell-actions{visibility:unset}.spectric-tooltip-portal{position:fixed;z-index:9999;pointer-events:none;--spectric-tooltip-background: color-mix(in srgb,var(--spectric-background-inverse,#f4f4f4) 100%,var(--spectric-primary,#1ea7fd) 90%) }.spectric-tooltip-portal .tooltip-container{display:flex;justify-content:center;align-items:center}.spectric-tooltip-portal.top .tooltip-container{flex-direction:column-reverse}.spectric-tooltip-portal.bottom .tooltip-container{flex-direction:column}.spectric-tooltip-portal.left .tooltip-container{flex-direction:row-reverse}.spectric-tooltip-portal .tooltip-content{background:var(--spectric-tooltip-background);border-radius:var(--spectric-border-radius,.4em);box-shadow:0 0 .01em .01em color-mix(in srgb,var(--spectric-background-hover,rgba(141, 141, 141, .12)) 90%,var(--spectric-text-on-color,#ffffff) 90%);padding:.2em;color:var(--spectric-text-on-color,#ffffff)}.spectric-tooltip-portal .tooltip-caret{background:var(--spectric-tooltip-background)}.spectric-tooltip-portal.top .tooltip-caret,.spectric-tooltip-portal.bottom .tooltip-caret{inline-size:.75rem;block-size:.374rem}.spectric-tooltip-portal.left .tooltip-caret,.spectric-tooltip-portal.right .tooltip-caret{inline-size:.375rem;block-size:.75rem}.spectric-tooltip-portal.top .tooltip-caret{clip-path:polygon(0 0,50% 100%,100% 0)}.spectric-tooltip-portal.bottom .tooltip-caret{clip-path:polygon(0 100%,50% 0,100% 100%)}.spectric-tooltip-portal.left .tooltip-caret{clip-path:polygon(0 0,100% 50%,0 100%)}.spectric-tooltip-portal.right .tooltip-caret{clip-path:polygon(0 50%,100% 0,100% 100%)}
1
+ spectric-input{--input-color: var(--spectric-input-color, #f4f4f4);--border-radius: var(--spectric-border-radius, .4em);--input-bottom: var(--spectric-input-bottom, var(--spectric-button-primary, #a8a8a8));--input-bottom-focused: var(--primary, #1ea7fd);--text-on-color: var(--spectric-text-on-color, #ffffff);--text-on-color-disabled: var(--spectric-text-on-color-disabled, #8d8d8d);--text-placeholder: rgba(22, 22, 22, .4);--text-primary: var(--spectric-text-primary, #161616);--text-secondary: var(--spectric-text-secondary, #525252)}spectric-input .inputWrapper{color:var(--text-secondary)}spectric-input .inputWrapper input{box-sizing:border-box;margin:0;vertical-align:baseline;font-size:.875rem;font-weight:400;line-height:1.28572;letter-spacing:.16px;outline:transparent solid 2px;outline-offset:-2px;border:none;padding:0 1rem;background-color:var(--input-color);color:var(--text-primary, #161616);font-family:inherit;inline-size:100%;block-size:2.5rem}spectric-input .inputWrapper .inputContainer:active:after,spectric-input .inputContainer:focus-within:after{border-bottom-color:var(--input-bottom-focused);width:calc(100% - 5px);transition:width .4s ease-in-out}spectric-input .inputWrapper input:read-only{background-color:transparent;border-bottom-color:var(--border-disabled)}spectric-input .inputContainer{position:relative;border-radius:var(--border-radius);overflow:hidden}spectric-input .inputContainer:after{content:"";width:0px;transition:background-color .4s cubic-bezier(.2,0,.38,.9),border-bottom-color .4s cubic-bezier(.2,0,.38,.9);border-bottom-color:var(--input-bottom);border-bottom-style:solid;border-bottom-width:1px;position:absolute;left:2.5px;bottom:0}spectric-input #helper-text{height:18px}spectric-input[variant=password] spectric-button{position:absolute;right:4px;bottom:3px}spectric-input .checkbox{display:flex;justify-self:center}spectric-query{font-family:monospace}spectric-query .autocomplete{color:var(--spectric-text-primary, #161616);border-radius:0em 0em var(--spectric-border-radius, .4em) var(--spectric-border-radius, .4em);background-color:var(--spectric-background, #ffffff);border:1px solid var(--spectric-background-hover, rgba(141, 141, 141, .12));max-height:300px;border-top:0px;margin:-18px 0 0;position:fixed;top:anchor(bottom);justify-self:anchor-center;text-align:center}spectric-query .autocomplete .optiontype{float:left;max-width:10px}spectric-query .autocomplete .label{position:absolute;right:0}spectric-query .autocomplete .option.active,spectric-query .autocomplete .option:hover{background-color:var(--spectric-background-hover, rgba(141, 141, 141, .12));border-bottom:1px solid var(--primary, #1ea7fd)}spectric-query .autocomplete .option{border-bottom:1px solid transparent;padding:8px}.query-bar-date-quick-select{display:flex;justify-content:space-evenly}spectric-pagination .spectric-pagination-container{display:flex;justify-content:space-between;align-items:center}spectric-pagination .spectric-pagination-text{flex-grow:1;text-align:center}spectric-table{display:flex;flex-direction:column;overflow:hidden}spectric-table .table-wrapper{overflow:auto;flex-grow:1;position:relative}spectric-table tr{text-align:center}spectric-table tr.odd{background-color:color-mix(in srgb,var(--spectric-primary, #1ea7fd),transparent 90%)}spectric-table tr:hover{background-color:color-mix(in srgb,var(--spectric-primary, #1ea7fd),transparent 70%)}spectric-table-header{display:table-header-group;font-weight:700;position:sticky;top:0;left:0;z-index:1;background:var(--spectric-background, #ffffff)}spectric-table-header td{vertical-align:middle}spectric-table tr{line-height:var(--rowHeight)}spectric-table td{height:var(--rowHeight)}spectric-table-header .header-contents{position:relative}spectric-table-header .header-contents .sort-direction{position:absolute;right:0}spectric-table-header .header-contents.sortable{cursor:pointer}spectric-table-header .header-contents.sortable:hover .sort-direction.none:before{content:"⮁"}spectric-table div[role=table]{display:table;min-width:100%}spectric-table-cell{display:contents;vertical-align:middle}spectric-table-cell td{position:relative}spectric-table td:hover:has(.filterable){border:1px solid var(--spectric-primary, #1ea7fd)}spectric-table td{border:1px solid transparent}spectric-table-cell .table-cell-actions{position:absolute;display:flex;width:100%;flex-direction:row-reverse;visibility:hidden;top:-10px}spectric-table-cell td:hover .table-cell-actions{visibility:unset}spectric-table .table-checkbox-single spectric-button{--button-border-radius: 50%}spectric-input.table-checkbox-single[checked] spectric-button{--text-on-color: transparent;border-radius:50%;position:relative}spectric-input.table-checkbox-single[checked] spectric-button:before{position:absolute;content:" ";height:50%;width:50%;left:25%;top:25%;border-radius:50%;z-index:1;box-shadow:0 0 0 4px var(--input-color)}spectric-table-body{display:table-row-group}spectric-table-virtual-body{display:contents}spectric-table-virtual-body .virtual-scroll-spacer td{padding:0;border:0px}.spectric-tooltip-portal{position:fixed;z-index:9999;pointer-events:none;--spectric-tooltip-background: color-mix(in srgb,var(--spectric-background-inverse,#f4f4f4) 100%,var(--spectric-primary,#1ea7fd) 90%) }.spectric-tooltip-portal .tooltip-container{display:flex;justify-content:center;align-items:center}.spectric-tooltip-portal.top .tooltip-container{flex-direction:column-reverse}.spectric-tooltip-portal.bottom .tooltip-container{flex-direction:column}.spectric-tooltip-portal.left .tooltip-container{flex-direction:row-reverse}.spectric-tooltip-portal .tooltip-content{background:var(--spectric-tooltip-background);border-radius:var(--spectric-border-radius,.4em);box-shadow:0 0 .01em .01em color-mix(in srgb,var(--spectric-background-hover,rgba(141, 141, 141, .12)) 90%,var(--spectric-text-on-color,#ffffff) 90%);padding:.2em;color:var(--spectric-text-on-color,#ffffff)}.spectric-tooltip-portal .tooltip-caret{background:var(--spectric-tooltip-background)}.spectric-tooltip-portal.top .tooltip-caret,.spectric-tooltip-portal.bottom .tooltip-caret{inline-size:.75rem;block-size:.374rem}.spectric-tooltip-portal.left .tooltip-caret,.spectric-tooltip-portal.right .tooltip-caret{inline-size:.375rem;block-size:.75rem}.spectric-tooltip-portal.top .tooltip-caret{clip-path:polygon(0 0,50% 100%,100% 0)}.spectric-tooltip-portal.bottom .tooltip-caret{clip-path:polygon(0 100%,50% 0,100% 100%)}.spectric-tooltip-portal.left .tooltip-caret{clip-path:polygon(0 0,100% 50%,0 100%)}.spectric-tooltip-portal.right .tooltip-caret{clip-path:polygon(0 50%,100% 0,100% 100%)}
@@ -0,0 +1 @@
1
+ export declare function once(func: Function): (...args: any[]) => any;
@@ -3,7 +3,7 @@ import { AsyncDirective } from 'lit/async-directive.js';
3
3
  /**
4
4
  * Usage:
5
5
  * import { html, render } from 'lit';
6
- * import { spreadProps } from '@open-wc/lit-helpers';
6
+ * import { spreadProps } from '@spectric/ui';
7
7
  *
8
8
  * render(
9
9
  * html`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spectric/ui",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "type": "module",
5
5
  "main": "./dist/index.es.js",
6
6
  "module": "./dist/index.es.js",
@@ -3,16 +3,17 @@ import { LitElement } from "lit-element/lit-element.js";
3
3
  export interface IDisposable {
4
4
  dispose(): void;
5
5
  }
6
+ type DisposableTarget = HTMLElement | Promise<HTMLElement> | (() => HTMLElement)
7
+ let listeners = 0;
6
8
  class DomListener implements IDisposable {
7
9
 
8
10
  private _handler?: (e: any) => void;
9
11
  private _node?: EventTarget;
10
- private __target?: EventTarget | Promise<EventTarget>
11
12
  private readonly _type: string;
12
13
  private readonly _options: boolean | AddEventListenerOptions;
13
14
 
14
- constructor(node: EventTarget | Promise<EventTarget>, type: string, handler: (e: any) => void, options?: boolean | AddEventListenerOptions) {
15
- this.__target = node
15
+ constructor(node: DisposableTarget, type: string, handler: (e: any) => void, options?: boolean | AddEventListenerOptions) {
16
+
16
17
  this._handler = handler;
17
18
  this._type = type;
18
19
  this._handler = handler;
@@ -26,10 +27,15 @@ class DomListener implements IDisposable {
26
27
  this._node = targetNode
27
28
  this._node.addEventListener(this._type, this._handler, this._options);
28
29
  })
30
+ } else if (typeof node === "function") {
31
+ this._node = node()
32
+ this._node.addEventListener(this._type, this._handler, this._options);
29
33
  } else {
30
34
  this._node = node
31
35
  this._node.addEventListener(this._type, this._handler, this._options);
32
36
  }
37
+ listeners += 1
38
+ //console.log(listeners)
33
39
  }
34
40
 
35
41
  dispose(): void {
@@ -38,19 +44,19 @@ class DomListener implements IDisposable {
38
44
  return;
39
45
  }
40
46
  if (this._node) {
41
- console.log(`removing event ${this._type} from `, this._node, this.__target instanceof Promise)
47
+ listeners -= 1
42
48
  this._node.removeEventListener(this._type, this._handler, this._options);
43
49
  }
44
50
  // Prevent leakers from holding on to the dom or handler func
45
- this.__target = undefined;
46
51
  this._node = undefined;
47
52
  this._handler = undefined;
53
+ //console.log(listeners)
48
54
  }
49
55
  }
50
56
  export class DisposableElement extends LitElement {
51
57
  private readonly _disposables = new Set<IDisposable>();
52
58
  private _isDisposed = false;
53
- private _disposableListeners: { target: HTMLElement | Promise<HTMLElement>, event: string, handler: any }[] = []
59
+ private _disposableListeners: { target: DisposableTarget, event: string, handler: any }[] = []
54
60
  private _connected: boolean = false;
55
61
  /**
56
62
  * Creates listeners on a target that will automatically get cleaned up when the element is removed from the DOM. This prevents memory leaks.
@@ -58,9 +64,9 @@ export class DisposableElement extends LitElement {
58
64
  * @param event The event name
59
65
  * @param handler The event handler
60
66
  */
61
- public addDisposableListener<K extends keyof GlobalEventHandlersEventMap>(target: HTMLElement | Promise<HTMLElement>, event: K, handler: (event: GlobalEventHandlersEventMap[K]) => void): void
62
- public addDisposableListener<K extends keyof SpectricGlobalEvents>(target: HTMLElement | Promise<HTMLElement>, event: K, handler: SpectricGlobalEvents[K]): void
63
- public addDisposableListener(target: HTMLElement | Promise<HTMLElement>, event: string, handler: any): void {
67
+ public addDisposableListener<K extends keyof GlobalEventHandlersEventMap>(target: DisposableTarget, event: K, handler: (event: GlobalEventHandlersEventMap[K]) => void): void
68
+ public addDisposableListener<K extends keyof SpectricGlobalEvents>(target: DisposableTarget, event: K, handler: SpectricGlobalEvents[K]): void
69
+ public addDisposableListener(target: DisposableTarget, event: string, handler: any): void {
64
70
  let exists = this._disposableListeners.find(d => d.event === event && d.target === target && d.handler === handler)
65
71
  if (exists) {
66
72
  console.warn("Event handler already exists best practice is to add the this in the constructor")
@@ -12,13 +12,13 @@ export interface BitDisplayProps {
12
12
  /** Array buffer to display */
13
13
  arrayBuffer: ArrayBuffer;
14
14
  /** Bits per line */
15
- frameWidth: number;
15
+ frameWidth?: number;
16
16
  /** How many pixels per bit */
17
- scale: number;
17
+ scale?: number;
18
18
  /** Width of the display canvas */
19
- width: number;
19
+ width?: number;
20
20
  /** Height of the display canvas */
21
- height: number;
21
+ height?: number;
22
22
 
23
23
  }
24
24
  type Position = {
@@ -340,7 +340,7 @@ export class BitDisplayCanvas extends DisposableElement implements BitDisplayPro
340
340
  id="viewport"
341
341
  ${ref(this.refs.viewport)}
342
342
  @scroll=${this.handleScroll}
343
- style=${`width:${width}px;height:${height}px;overflow:auto;position:relative`}
343
+ style=${`width:${width + SCROLLBAR_SIZE.width}px;height:${height + SCROLLBAR_SIZE.height}px;overflow:auto;position:relative`}
344
344
  >
345
345
  <div
346
346
  id="fakescrolldiv"
@@ -350,8 +350,8 @@ export class BitDisplayCanvas extends DisposableElement implements BitDisplayPro
350
350
  </div>
351
351
  <canvas
352
352
  ${ref(this.refs.canvas)}
353
- width=${width - SCROLLBAR_SIZE.width}
354
- height=${height - SCROLLBAR_SIZE.height}
353
+ width=${width}
354
+ height=${height}
355
355
  ></canvas>
356
356
  `
357
357
 
@@ -29,7 +29,7 @@ export interface ButtonProps {
29
29
  /** What background color to use */
30
30
  backgroundColor?: string;
31
31
  /** How large should the button be? */
32
- size: ButtonSizesTypes;
32
+ size?: ButtonSizesTypes;
33
33
  label?: string,
34
34
  /* should the button be disabled*/
35
35
  disabled?: boolean;
@@ -10,7 +10,7 @@ import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from './type
10
10
  * Properties defining
11
11
  */
12
12
  export interface HeaderProps {
13
- username: string;
13
+ username?: string;
14
14
  /**
15
15
  * Shows the login Button
16
16
  */
@@ -1,4 +1,4 @@
1
- import { html, LitElement, TemplateResult } from 'lit';
1
+ import { html, LitElement, PropertyValues, TemplateResult } from 'lit';
2
2
  import "./input.css"
3
3
  import { customElement, property, query } from 'lit/decorators.js';
4
4
  import { ifDefined } from 'lit/directives/if-defined.js';
@@ -23,32 +23,32 @@ export enum InputVariants {
23
23
  type InputVariantsTypes = `${InputVariants}`
24
24
  export interface InputProps {
25
25
  /** Input type */
26
- variant: InputVariantsTypes;
26
+ variant?: InputVariantsTypes;
27
27
  /**Label to display above the input */
28
- label: string,
28
+ label?: string,
29
29
  /**placeholder text to display*/
30
- placeholder: string,
30
+ placeholder?: string,
31
31
  /* should be disabled?*/
32
- disabled: boolean,
32
+ disabled?: boolean,
33
33
  /* should be readonly*/
34
- readonly: boolean,
34
+ readonly?: boolean,
35
35
  /**
36
36
  * The helper text.
37
37
  */
38
38
 
39
- helperText: string,
39
+ helperText?: string,
40
40
  /**
41
41
  * Specify if the currently value is invalid.
42
42
  */
43
- invalid: boolean,
43
+ invalid?: boolean,
44
44
  /**
45
45
  * Message which is displayed if the value is invalid.
46
46
  */
47
- invalidText: string | TemplateResult<1>,
47
+ invalidText?: string | TemplateResult<1>,
48
48
  /**
49
49
  * Max character count allowed for input. This is needed in order for enableCounter to display
50
50
  */
51
- maxCount: number,
51
+ maxCount?: number,
52
52
  /**
53
53
  * Boolean property to set the required status
54
54
  */
@@ -71,7 +71,7 @@ export interface InputProps {
71
71
  /**
72
72
  * The sets the autocomplete for the input.
73
73
  */
74
- autocomplete: HTMLInputElement['autocomplete'];
74
+ autocomplete?: HTMLInputElement['autocomplete'];
75
75
  }
76
76
  @customElement('spectric-input')
77
77
  export class SpectricInput extends LitElement implements InputProps {
@@ -148,7 +148,7 @@ export class SpectricInput extends LitElement implements InputProps {
148
148
  @property()
149
149
  pattern = '';
150
150
  @property({ type: Number, reflect: true })
151
- maxCount: InputProps['maxCount'] = 0;
151
+ maxCount: number = 0;
152
152
  /**
153
153
  * The internal value.
154
154
  */
@@ -158,7 +158,7 @@ export class SpectricInput extends LitElement implements InputProps {
158
158
  * The sets the autocomplete for the input.
159
159
  */
160
160
  @property({ reflect: true })
161
- autocomplete: HTMLInputElement['autocomplete'] = "";
161
+ autocomplete: HTMLInputElement['autocomplete'] = "off";
162
162
  get selectionStart() {
163
163
  if (!this._input) {
164
164
  return null
@@ -227,6 +227,11 @@ export class SpectricInput extends LitElement implements InputProps {
227
227
  this._showPassword = !this._showPassword;
228
228
  this.requestUpdate()
229
229
  }
230
+ protected updated(changedProperties: PropertyValues): void {
231
+ if (changedProperties.has("checked")) {
232
+ this.dispatchEvent(new Event("change", { bubbles: true }))
233
+ }
234
+ }
230
235
  protected render(): unknown {
231
236
  switch (this.variant) {
232
237
  case InputVariants.Text:
@@ -303,8 +308,6 @@ export class SpectricInput extends LitElement implements InputProps {
303
308
  <spectric-button @click=${() => {
304
309
  this.checked = !this.checked;
305
310
  this.value = Boolean(this.checked);
306
- console.log(this.checked, this.value)
307
- this.dispatchEvent(new Event("change", { bubbles: true }))
308
311
  }} icon size=${this.size || "xxsmall"} variant=${this.checked ? "primary" : "secondary"}>${this.checked ? '✓' : "\u00A0"}</spectric-button>
309
312
  ${this.invalid || this.helperText ? html`<spectric-tooltip text=${this.invalid || this.helperText}></spectric-tooltip>` : null}
310
313
  ${this.label}
@@ -8,12 +8,12 @@ import { ButtonSizesTypes } from '../Button';
8
8
  export type { PaginationProps, PaginationChangeProps, PaginationEvents }
9
9
 
10
10
  interface PaginationChangeProps {
11
- page: number;
12
- pageSize: number;
11
+ page?: number;
12
+ pageSize?: number;
13
13
  }
14
14
 
15
15
  interface PaginationProps extends PaginationChangeProps {
16
- size: ButtonSizesTypes
16
+ size?: ButtonSizesTypes
17
17
  totalItems?: number
18
18
  pageSizeOptions?: number[]
19
19
  }
@@ -93,8 +93,8 @@ export class PaginationElement extends LitElement implements PaginationProps {
93
93
  ${pageText}
94
94
  </div>
95
95
  <div>
96
- <spectric-button size=${this.size} ?disabled=${this.page === 1} @click=${this._handlePageDown} icon><</spectric-button>
97
- <spectric-button size=${this.size} ?disabled=${nextPageDisabled} @click=${this._handlePageUp} icon>></spectric-button>
96
+ <spectric-button size=${this.size} ?disabled=${this.page === 1} @click=${this._handlePageDown} icon>🠈</spectric-button>
97
+ <spectric-button size=${this.size} ?disabled=${nextPageDisabled} @click=${this._handlePageUp} icon>🠊</spectric-button>
98
98
  </div>
99
99
  </div>
100
100
  `: null}
@@ -115,7 +115,7 @@ declare global {
115
115
  namespace JSX {
116
116
  interface IntrinsicElements {
117
117
  /**
118
- * @see {@link DialogElement}
118
+ * @see {@link PaginationElement}
119
119
  */
120
120
  [PaginationElementTag]: ReactElementWithPropsAndEvents<PaginationElement, PaginationProps, PaginationEvents>;
121
121
  }
@@ -124,7 +124,7 @@ declare global {
124
124
  namespace JSX {
125
125
  interface IntrinsicElements {
126
126
  /**
127
- * @see {@link DialogElement}
127
+ * @see {@link PaginationElement}
128
128
  */
129
129
  [PaginationElementTag]: ReactElementWithPropsAndEvents<PaginationElement, PaginationProps, PaginationEvents>
130
130
  }
@@ -133,32 +133,34 @@ export class SpectricQuery extends LitElement implements IQueryProps {
133
133
  protected _input!: SpectricInput;
134
134
  _parseQuery = (e: InputEvent | undefined = undefined) => {
135
135
  let ast;
136
- if (e && e?.currentTarget && (e.currentTarget as HTMLInputElement).value) {
136
+ if (e && e?.currentTarget) {
137
137
  this.value = (e.currentTarget as HTMLInputElement).value
138
138
  }
139
- if (this.value == "") {
140
- return
139
+ if (this.value) {
140
+ try {
141
+ if (e && e.data == "(") {
142
+ //Auto close parentheses or parsing and suggestions fail
143
+ this.value = this.value + " )"
144
+ this._input.setSelectionRange(this.value.length - 2, this.value.length - 2)
145
+ }
146
+ let value = this.value;
147
+ if (this._input.selectionStart !== null) {
148
+ value = value.substring(0, this._input.selectionStart) + "@kuery-cursor@" + value.substring(this._input.selectionStart)
149
+ }
150
+ //FIXME: make auto complete work well.
151
+ let suggestions = kuery.parse(value, { parseCursor: true, cursorSymbol: "@kuery-cursor@", allowLeadingWildcards: false }) as unknown as Suggestion;
152
+ this.autoComplete(suggestions)
153
+ } catch (error: any) {
154
+ // this.completions = []
155
+ // this._input.invalid = true;
156
+ // let [expect, _, arrow] = e.message.split("\n")
157
+ // this._input.invalidText = html`&#160;&#160;${arrow} ${expect}`;
158
+ return
159
+ }
141
160
  }
142
161
  try {
143
- if (e && e.data == "(") {
144
- //Auto close parentheses or parsing and suggestions fail
145
- this.value = this.value + " )"
146
- this._input.setSelectionRange(this.value.length - 2, this.value.length - 2)
147
- }
148
- let value = this.value;
149
- if (this._input.selectionStart !== null) {
150
- value = value.substring(0, this._input.selectionStart) + "@kuery-cursor@" + value.substring(this._input.selectionStart)
151
- }
152
- //FIXME: make auto complete work well.
153
- let suggestions = kuery.parse(value, { parseCursor: true, cursorSymbol: "@kuery-cursor@", allowLeadingWildcards: false }) as unknown as Suggestion;
154
- this.autoComplete(suggestions)
155
-
156
162
  ast = kuery.parse(this.value, { allowLeadingWildcards: false });
157
- } catch (e: any) {
158
- // this.completions = []
159
- // this._input.invalid = true;
160
- // let [expect, _, arrow] = e.message.split("\n")
161
- // this._input.invalidText = html`&#160;&#160;${arrow} ${expect}`;
163
+ } catch (error: any) {
162
164
  return
163
165
  }
164
166
  let output
@@ -288,6 +290,8 @@ export class SpectricQuery extends LitElement implements IQueryProps {
288
290
  } else {
289
291
  this.value = prefix + this.value.substring(completion.end)
290
292
  }
293
+ //Important if the underlying inputs value hasn't been set the setSelectionRange will fail to set a range greater than the inputs current value
294
+ this._input.value = this.value
291
295
  this._input.setSelectionRange(insertIndex, insertIndex)
292
296
  if (completion.onSelect) {
293
297
  let value = await completion.onSelect()
@@ -299,6 +303,7 @@ export class SpectricQuery extends LitElement implements IQueryProps {
299
303
  this.completionIndex = 0;
300
304
  this.completions = []
301
305
  this._parseQuery()
306
+ this._input.focus()
302
307
  }
303
308
  _handleArrows = (e: KeyboardEvent) => {
304
309
  if (e.key === "Escape") {
@@ -12,15 +12,15 @@ export enum Orientations {
12
12
  }
13
13
  export interface SplitViewProps {
14
14
  /** Controls the orientation of the splitter handle */
15
- orientation: `${Orientations}`;
15
+ orientation?: `${Orientations}`;
16
16
  /** the percentage to split the view default: 50*/
17
17
  percentage?: number
18
18
  /** Should the splitter handle be invisible? */
19
19
  invisible?: boolean
20
20
  /** Clamps the minimum split percentage default: 10 */
21
- min: number
21
+ min?: number
22
22
  /** Clamps the maximum split percentage default: 90 */
23
- max: number
23
+ max?: number
24
24
  /** save and load split state to localstorage splitter must have an id attribute default: true */
25
25
  useSavedState?: boolean
26
26
  }
@@ -40,8 +40,8 @@ export class SplitView extends DisposableElement implements SplitViewProps {
40
40
  @property({ type: Boolean }) invisible: SplitViewProps['invisible'] = false;
41
41
  static styles = style
42
42
 
43
- @property({ type: Number, reflect: true }) min: SplitViewProps['min'] = 10;
44
- @property({ type: Number, reflect: true }) max: SplitViewProps['max'] = 90;
43
+ @property({ type: Number, reflect: true }) min: number = 10;
44
+ @property({ type: Number, reflect: true }) max: number = 90;
45
45
  @property({ type: Boolean, reflect: true }) useSavedState: SplitViewProps['useSavedState'] = true;
46
46
 
47
47
  @state()
@@ -1,9 +1,10 @@
1
- import { html, LitElement } from 'lit';
1
+ import { html, LitElement, PropertyValues } from 'lit';
2
2
  import { customElement, property, } from 'lit/decorators.js';
3
3
  import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from '../types';
4
4
  export const TableBodyElementTag = "spectric-table-body"
5
5
  import "./cell"
6
6
  import { ColumnSettings, TableElement } from './table';
7
+ import { repeat } from 'lit/directives/repeat.js';
7
8
 
8
9
  interface BodyProps<T> {
9
10
  columns: ColumnSettings<T>[]
@@ -24,14 +25,21 @@ export class TableBodyElement<T> extends LitElement implements BodyProps<T> {
24
25
  protected createRenderRoot(): HTMLElement | DocumentFragment {
25
26
  return this
26
27
  }
28
+ protected firstUpdated(_changedProperties: PropertyValues): void {
29
+ if (this.data.length > 100) {
30
+ console.warn("Table is rendering excessive amounts of data to the DOM you should set rowHeight and use a virtualized scroll or use pagination")
31
+ }
32
+ }
27
33
  protected render(): unknown {
28
34
  return html`
29
35
  <body>
30
- ${this.data.map((row: any) => {
31
- return html`<tr>${this.columns.map(col => {
32
- return html`<spectric-table-cell .column=${col} .row=${row} .columns=${this.columns}></spectric-table-cell>`
33
- })}</tr>`
36
+ ${repeat(this.data, (row, index) => html`
37
+ <tr class="${index % 2 === 0 ? "odd" : ""}">
38
+ ${repeat(this.columns, (col) => {
39
+ return html`<spectric-table-cell .column=${col} .index=${index} .row=${row} .columns=${this.columns}></spectric-table-cell>`
34
40
  })}
41
+ </tr>`)}
42
+
35
43
  </body>
36
44
  `
37
45
 
@@ -23,6 +23,9 @@ export type FilterEvent<T> = {
23
23
  export class TableCellElement<T> extends LitElement implements CellProps<T> {
24
24
  @property({ type: Object, attribute: false })
25
25
  row!: T;
26
+
27
+ @property({ type: Number, attribute: false })
28
+ index!: number;
26
29
  @property({ type: Object, attribute: false })
27
30
  column!: ColumnSettings<T>;
28
31
  columns!: ColumnSettings<T>[];
@@ -43,7 +46,7 @@ export class TableCellElement<T> extends LitElement implements CellProps<T> {
43
46
  _handleFilterOut = () => {
44
47
  let value = undefined;
45
48
  if (this.column.key && typeof this.row === "object") {
46
- value = getValue(this.row as Record<any, any>, this.column.key)
49
+ value = rowGetValue(this.row as Record<any, any>, this.column.key)
47
50
  }
48
51
  this.dispatchEvent(new CustomEvent<FilterEvent<T>>("filter", {
49
52
  composed: true,
@@ -59,7 +62,7 @@ export class TableCellElement<T> extends LitElement implements CellProps<T> {
59
62
  _handleFilterFor = () => {
60
63
  let value = undefined;
61
64
  if (this.column.key && typeof this.row === "object") {
62
- value = getValue(this.row as Record<any, any>, this.column.key)
65
+ value = rowGetValue(this.row as Record<any, any>, this.column.key)
63
66
  }
64
67
  this.dispatchEvent(new CustomEvent<FilterEvent<T>>("filter", {
65
68
  composed: true,
@@ -75,10 +78,9 @@ export class TableCellElement<T> extends LitElement implements CellProps<T> {
75
78
  protected render(): unknown {
76
79
  let rendered
77
80
  if (this.column.render) {
78
- rendered = this.column.render(this.row, this.table)
81
+ rendered = this.column.render(this.row, this.index, this.table)
79
82
  } else if (this.column.key && typeof this.row === 'object') {
80
-
81
- rendered = getValue(this.row as any, this.column.key)
83
+ rendered = rowGetValue(this.row as any, this.column.key)
82
84
  } else {
83
85
  rendered = html`error`
84
86
  }
@@ -134,11 +136,11 @@ declare global {
134
136
  }
135
137
 
136
138
 
137
- const getValue = (context: Record<string, any>, key: string) => {
139
+ export const rowGetValue = (context: Record<string, any>, key: string) => {
138
140
  let path = key.split(".")
139
141
  let value = context[path[0]]
140
142
  if (path.length > 1) {
141
- value = getValue(value, path.slice(1).join("."))
143
+ value = rowGetValue(value, path.slice(1).join("."))
142
144
  }
143
145
  return value
144
146
  }
@@ -4,7 +4,7 @@ import { customElement, property, } from 'lit/decorators.js';
4
4
  import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from '../types';
5
5
  import "./table.css"
6
6
  export const TableHeaderElementTag = "spectric-table-header"
7
- import { ColumnSettings } from './table';
7
+ import { ColumnSettings, TableSortDirection } from './table';
8
8
 
9
9
 
10
10
  interface HeaderProps<T> {
@@ -22,8 +22,21 @@ export class TableHeaderElement<T> extends LitElement implements HeaderProps<T>
22
22
  protected createRenderRoot(): HTMLElement | DocumentFragment {
23
23
  return this
24
24
  }
25
+ _handleSortChange = (column: ColumnSettings<T>) => {
26
+ column = JSON.parse(JSON.stringify(column))// Clone the column to not mutate the original object
27
+ if (!column.sortable) {
28
+ return
29
+ }
30
+ if (column.sortDirection === TableSortDirection.none || column.sortDirection === undefined) {
31
+ column.sortDirection = TableSortDirection.ascending
32
+ } else if (column.sortDirection === TableSortDirection.ascending) {
33
+ column.sortDirection = TableSortDirection.decending
34
+ } else if (column.sortDirection === TableSortDirection.decending) {
35
+ column.sortDirection = TableSortDirection.none
36
+ }
37
+ this.dispatchEvent(new CustomEvent<ColumnSettings<T>>("sortChange", { detail: column }))
38
+ }
25
39
  protected render(): unknown {
26
-
27
40
  return html`
28
41
  <tr>
29
42
  ${this.columns.map(column => {
@@ -31,7 +44,18 @@ export class TableHeaderElement<T> extends LitElement implements HeaderProps<T>
31
44
  if (column.filterable) {
32
45
  //classes.push("filterable")
33
46
  }
34
- return html`<td><div class=${classes.join(" ")}>${column.title || column.key}</div></td>`
47
+ if (column.sortable) {
48
+ classes.push("sortable")
49
+ }
50
+ let columnWidth = column.width ? `width:${column.width}px;` : ""
51
+ let sortDirection = column.sortDirection === TableSortDirection.ascending ? `🠉` : column.sortDirection == TableSortDirection.decending ? `🠋` : ``
52
+ let sortClass = column.sortDirection || TableSortDirection.none
53
+ return html`
54
+ <td @click=${() => this._handleSortChange(column)} style="${columnWidth}">
55
+ <div class=${classes.join(" ")} >${column.title || column.key}
56
+ <span class="sort-direction ${sortClass}">${sortDirection}</span>
57
+ </div>
58
+ </td>`
35
59
  })}
36
60
  </tr>
37
61
  `
@@ -39,7 +63,7 @@ export class TableHeaderElement<T> extends LitElement implements HeaderProps<T>
39
63
  }
40
64
 
41
65
  interface TableHeaderEvents {
42
- 'sort': (event: CustomEvent<ColumnSettings<any>>) => void; //TODO sort events
66
+ 'sortChange': (event: CustomEvent<ColumnSettings<any>>) => void; //TODO sort events
43
67
  }
44
68
 
45
69
  declare global {
@@ -0,0 +1,34 @@
1
+ import { once } from "../../utils/once";
2
+ import { rowGetValue } from "./cell";
3
+ import { ColumnSettings, TableSortDirection } from "./table";
4
+
5
+ /**
6
+ * Creates a chain of sort functions so you can perform multi column sorting.
7
+ */
8
+ export const createSortChain = <T>(sorts: ColumnSettings<T>[]) => {
9
+ return sorts.map(({ key, sortDirection, compareFn }) => {
10
+ return (a: T, b: T) => {
11
+ if (!key) {
12
+ return undefined
13
+ }
14
+ let v1 = rowGetValue(a as Record<string, any>, key)
15
+ let v2 = rowGetValue(b as Record<string, any>, key)
16
+ if (compareFn) {
17
+ return compareFn(a, b)
18
+ }
19
+ if (typeof v1 === "number" || typeof v1 === "bigint" || typeof v1 === "boolean") {
20
+ //@ts-ignore
21
+ let sort: number = v1 - v2 as unknown as number
22
+ if (sortDirection === TableSortDirection.decending) {
23
+ sort = sort * -1
24
+ }
25
+ return sort
26
+ } else if (typeof v1 === "string") {
27
+ return v1.localeCompare(v2) * (sortDirection === TableSortDirection.decending ? -1 : 1)
28
+ }
29
+ once(() => { console.error(`Unable to sort type ${typeof v1}`) })
30
+ return undefined
31
+
32
+ }
33
+ })
34
+ }