@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.
Files changed (51) hide show
  1. package/dist/classes/DisposibleElement.d.ts +1 -0
  2. package/dist/classes/index.d.ts +2 -0
  3. package/dist/components/ThemeProvider.d.ts +1 -0
  4. package/dist/components/color_picker/ColorPicker.d.ts +59 -0
  5. package/dist/components/color_picker/index.d.ts +0 -0
  6. package/dist/components/index.d.ts +2 -0
  7. package/dist/components/input.d.ts +3 -1
  8. package/dist/components/pagination/pagination.d.ts +1 -1
  9. package/dist/components/table/body.d.ts +2 -2
  10. package/dist/components/table/cell.d.ts +2 -2
  11. package/dist/components/table/header.d.ts +9 -3
  12. package/dist/components/table/table.d.ts +18 -6
  13. package/dist/components/table/virtualBody.d.ts +4 -3
  14. package/dist/components/tooltip/popover.d.ts +85 -0
  15. package/dist/components/tooltip/tooltip.d.ts +14 -15
  16. package/dist/custom-elements.json +83 -8
  17. package/dist/index.d.ts +59 -0
  18. package/dist/index.es.js +2675 -2245
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +189 -135
  21. package/dist/index.umd.js.map +1 -1
  22. package/dist/style.css +1 -1
  23. package/dist/utils/index.d.ts +3 -0
  24. package/package.json +6 -2
  25. package/src/classes/DisposibleElement.ts +3 -0
  26. package/src/classes/index.ts +2 -0
  27. package/src/components/Button.ts +1 -1
  28. package/src/components/ThemeProvider.ts +34 -1
  29. package/src/components/button.css.ts +2 -2
  30. package/src/components/color_picker/ColorPicker.css +40 -0
  31. package/src/components/color_picker/ColorPicker.ts +268 -0
  32. package/src/components/color_picker/index.ts +1 -0
  33. package/src/components/index.ts +3 -1
  34. package/src/components/input.css +10 -2
  35. package/src/components/input.ts +38 -9
  36. package/src/components/pagination/pagination.ts +2 -2
  37. package/src/components/table/__tests__/table.spec.ts +91 -0
  38. package/src/components/table/body.ts +2 -2
  39. package/src/components/table/cell.ts +11 -5
  40. package/src/components/table/header.css +54 -0
  41. package/src/components/table/header.ts +141 -49
  42. package/src/components/table/table.css +11 -34
  43. package/src/components/table/table.ts +51 -17
  44. package/src/components/table/virtualBody.ts +18 -7
  45. package/src/components/tooltip/popover.ts +221 -0
  46. package/src/components/tooltip/tooltip.css +21 -16
  47. package/src/components/tooltip/tooltip.ts +18 -124
  48. package/src/index.ts +8 -1
  49. package/src/stories/fixtures/ExampleContent.ts +4 -3
  50. package/src/stories/table.stories.ts +6 -7
  51. package/src/utils/index.ts +3 -0
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;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;top:calc(50% - 8px)}spectric-table-header .header-contents.sortable{cursor:pointer;padding-right:15px}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;z-index:1}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 .cell-contents{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;overflow:hidden;text-overflow:ellipsis}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%)}
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 #helper-text.hidden{display:none}spectric-input[variant=password] spectric-button{position:absolute;right:4px;bottom:3px}spectric-input .checkbox{display:flex;justify-self:center}spectric-input spectric-colorpicker{display:block}spectric-input[variant=color] .fieldwrapper{display:inline-block}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;line-height:1}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 spectric-table-virtual-body tr:hover{background-color:color-mix(in srgb,var(--spectric-primary, #1ea7fd),transparent 70%)}spectric-table tr{height:var(--rowHeight)}spectric-table td{height:var(--rowHeight);padding:1px;border:1px solid transparent}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-cell .table-cell-actions{position:absolute;display:flex;width:100%;flex-direction:row-reverse;visibility:hidden;top:-10px;z-index:1}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 .cell-contents{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--lineClamp,1);-webkit-box-pack:center;overflow:hidden;text-overflow:ellipsis;font-size:var(--fontSize);height:calc(var(--lineClamp) * var(--fontSize))}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;position:relative}spectric-table-header .header-contents{position:relative}spectric-table-header .header-contents .sort-direction{position:absolute;right:2px;top:calc(50% - 8px)}spectric-table-header .header-contents.sortable{cursor:pointer;padding-right:15px}spectric-table-header .header-contents.resizing{-webkit-user-select:none;user-select:none}spectric-table-header .header-contents.sortable:hover .sort-direction.none:before{content:"⮁"}spectric-table-header td .header-resize-handle{width:2px;position:absolute;right:-1px;top:1px;visibility:hidden;background-color:var(--spectric-primary, #1ea7fd);height:100%;cursor:ew-resize;z-index:1}spectric-table-header td:hover .header-resize-handle{visibility:visible}spectric-table-header td:hover:has(*.header-resize-handle){border-bottom:1px solid var(--spectric-primary, #1ea7fd)}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-popover-portal{position:fixed;z-index:9999;--spectric-tooltip-background-color: var(--spectric-tooltip-background, var(--spectric-background,#000000));--spectric-tooltip-text-color: var(--spectric-tooltip-text, var(--spectric-text-primary, white));--spectric-tooltip-accent-color: var(--spectric-tooltip-accent, var(--spectric-primary, #1ea7fd))}.spectric-popover-portal.spectric-tooltip-portal{pointer-events:none}.spectric-popover-portal .tooltip-container{display:flex;justify-content:center;align-items:center}.spectric-popover-portal.top .tooltip-container{flex-direction:column-reverse}.spectric-popover-portal.bottom .tooltip-container{flex-direction:column}.spectric-popover-portal.left .tooltip-container{flex-direction:row-reverse}.spectric-popover-portal .tooltip-content{background:var(--spectric-tooltip-background-color);border-radius:var(--spectric-border-radius,.4em);border:1px solid color-mix(in srgb,var(--spectric-tooltip-background-color) 90%,var(--spectric-tooltip-accent-color) 90%);box-shadow:0 0 .01em .01em color-mix(in srgb,var(--spectric-tooltip-accent-color) 90%,var(--spectric-text-on-color,#ffffff) 90%);padding:.2em;color:var(--spectric-tooltip-text-color)}.spectric-popover-portal .tooltip-caret{background:color-mix(in srgb,var(--spectric-tooltip-background-color) 90%,var(--spectric-tooltip-accent-color) 90%)}.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-popover-portal.top .tooltip-caret{clip-path:polygon(0 0,50% 100%,100% 0)}.spectric-popover-portal.bottom .tooltip-caret{clip-path:polygon(0 100%,50% 0,100% 100%)}.spectric-popover-portal.left .tooltip-caret{clip-path:polygon(0 0,100% 50%,0 100%)}.spectric-popover-portal.right .tooltip-caret{clip-path:polygon(0 50%,100% 0,100% 100%)}spectric-input.hue-gradient input[type=range]::-webkit-slider-runnable-track{background:linear-gradient(to right,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}spectric-input.alpha-gradient input[type=range]::-webkit-slider-runnable-track{background:linear-gradient(to right,#fff0,#fff),repeating-conic-gradient(#fff,#fff 25%,#000 25%,#000 50%,#fff 50%,#fff 75%,#000 75%,#000) 50% / 10px 10px}spectric-input.alpha-gradient input[type=range],spectric-input.hue-gradient input[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none}.color-picker-footer{display:flex;justify-content:space-evenly}.color-picker-saturation-lightness-grid{cursor:crosshair}.color-picker-eyedropper{--button-border-radius:50% }
@@ -0,0 +1,3 @@
1
+ export * from './debounce';
2
+ export * from './once';
3
+ export * from './spread';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spectric/ui",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "type": "module",
5
5
  "main": "./dist/index.es.js",
6
6
  "module": "./dist/index.es.js",
@@ -13,14 +13,18 @@
13
13
  "scripts": {
14
14
  "build": "tsc && vite build",
15
15
  "start": "storybook dev -p 6006",
16
+ "test-setup": "storybook dev -p 6006 --no-open --ci --loglevel verbose",
16
17
  "storybook:build": "web-component-analyzer src --outFiles .storybook/build/custom-elements.json && storybook build --output-dir public",
17
- "release": "npm version patch && git push --follow-tags"
18
+ "release": "npm version patch && git push --follow-tags",
19
+ "test": "playwright test --reporter=list --timeout 90000",
20
+ "type-check": "tsc --noemit"
18
21
  },
19
22
  "dependencies": {
20
23
  "lit": "^3.2.1"
21
24
  },
22
25
  "devDependencies": {
23
26
  "@chromatic-com/storybook": "^3.2.2",
27
+ "@playwright/test": "^1.55.0",
24
28
  "@storybook/addon-essentials": "^8.4.6",
25
29
  "@storybook/blocks": "^8.4.6",
26
30
  "@storybook/client-api": "^7.6.17",
@@ -5,6 +5,9 @@ export interface IDisposable {
5
5
  }
6
6
  type DisposableTarget = HTMLElement | Promise<HTMLElement> | (() => HTMLElement)
7
7
  let listeners = 0;
8
+ export const getListeners = () => {
9
+ return listeners
10
+ }
8
11
  class DomListener implements IDisposable {
9
12
 
10
13
  private _handler?: (e: any) => void;
@@ -0,0 +1,2 @@
1
+ export * from "./BitArray"
2
+ export * from "./DisposibleElement"
@@ -93,7 +93,7 @@ export class SpectricButton extends DisposableElement implements ButtonProps {
93
93
  protected render(): unknown {
94
94
  const mode = MODES[this.variant] || 'spectric-button--primary';
95
95
  return html`
96
- ${this.tooltip ? html`<spectric-tooltip .text=${this.tooltip} .position=${this.tooltipPosition || "right"} .triggerTarget=${this}></spectric-tooltip>` : null}
96
+ ${this.tooltip && this.tooltip !== "" ? html`<spectric-tooltip .text=${this.tooltip} .position=${this.tooltipPosition || "right"} .triggerTarget=${this}></spectric-tooltip>` : null}
97
97
  <button
98
98
  type="button"
99
99
  ?disabled=${this.disabled}
@@ -65,7 +65,40 @@ type ThemeProps = {
65
65
  theme?: KnownThemes
66
66
  }
67
67
 
68
-
68
+ export const SPECTRIC_CSS_VARIABLES = ["--spectric-background",
69
+ "--spectric-background-hover",
70
+ "--spectric-background-inverse",
71
+ "--spectric-background-inverse-hover",
72
+ "--spectric-border-radius",
73
+ "--spectric-primary",
74
+ "--spectric-secondary",
75
+ "--spectric-tertiary",
76
+ "--spectric-disabled",
77
+ "--panel-color",
78
+ "--panel-color-inverse",
79
+ "--spectric-border-disabled",
80
+ "--spectric-input-color",
81
+ "--spectric-input-bottom",
82
+ "--spectric-text-on-color",
83
+ "--spectric-text-on-color-disabled",
84
+ "--spectric-text-placeholder",
85
+ "--spectric-text-primary",
86
+ "--spectric-text-secondary",
87
+ "--spectric-button-separator",
88
+ "--spectric-button-primary",
89
+ "--spectric-button-secondary",
90
+ "--spectric-button-tertiary",
91
+ "--spectric-button-danger-primary",
92
+ "--spectric-button-danger-secondary",
93
+ "--spectric-button-danger-active",
94
+ "--spectric-button-primary-active",
95
+ "--spectric-button-secondary-active",
96
+ "--spectric-button-tertiary-active",
97
+ "--spectric-button-danger-hover",
98
+ "--spectric-button-primary-hover",
99
+ "--spectric-button-secondary-hover",
100
+ "--spectric-button-tertiary-hover",
101
+ "--spectric-text-on-color-disabled"]
69
102
  const theme = `
70
103
  --spectric-background: var(--background,#ffffff);
71
104
  --spectric-background-hover:var(--background-hover,rgba(141, 141, 141, 0.12));
@@ -116,8 +116,8 @@ export default css`
116
116
  height: 10px;
117
117
  }
118
118
  :host([icon][size="xxsmall"]) .spectric-button{
119
- width: 20px;
120
- height: 20px;
119
+ width: 16px;
120
+ height: 16px;
121
121
  }
122
122
  :host([icon][size="xsmall"]) .spectric-button{
123
123
  width: 28px;
@@ -0,0 +1,40 @@
1
+ spectric-input.hue-gradient input[type="range"]::-webkit-slider-runnable-track {
2
+ background: linear-gradient(
3
+ to right,
4
+ hsl(0, 100%, 50%), /* Red */
5
+ hsl(60, 100%, 50%), /* Yellow */
6
+ hsl(120, 100%, 50%), /* Green */
7
+ hsl(180, 100%, 50%), /* Cyan */
8
+ hsl(240, 100%, 50%), /* Blue */
9
+ hsl(300, 100%, 50%), /* Magenta */
10
+ hsl(360, 100%, 50%) /* Red (completes the circle) */
11
+ )
12
+ }
13
+
14
+ spectric-input.alpha-gradient input[type="range"]::-webkit-slider-runnable-track {
15
+ background:
16
+ /* Alpha gradient from transparent to opaque */
17
+ linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%),
18
+ /* Checkerboard */
19
+ repeating-conic-gradient(
20
+ #fff 0% 25%, /* White for the first quarter */
21
+ #000 25% 50%, /* Black for the second quarter */
22
+ #fff 50% 75%, /* White for the third quarter */
23
+ #000 75% 100% /* Black for the fourth quarter */
24
+ ) 50% / 10px 10px;
25
+
26
+ }
27
+ spectric-input.alpha-gradient input[type="range"],spectric-input.hue-gradient input[type="range"]{ -webkit-appearance: none; /* For WebKit browsers (Chrome, Safari) */
28
+ -moz-appearance: none; /* For Mozilla Firefox */
29
+ appearance: none; /* Standard property */}
30
+
31
+ .color-picker-footer{
32
+ display: flex;
33
+ justify-content: space-evenly;
34
+ }
35
+ .color-picker-saturation-lightness-grid{
36
+ cursor: crosshair;
37
+ }
38
+ .color-picker-eyedropper{
39
+ --button-border-radius:50%
40
+ }
@@ -0,0 +1,268 @@
1
+ import { html, LitElement, PropertyValues, TemplateResult } from 'lit';
2
+ import { customElement, property, state } from 'lit/decorators.js';
3
+ import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from '../types';
4
+ import { PopoverElement } from '../tooltip/popover';
5
+ import { createRef, ref } from 'lit/directives/ref.js';
6
+ import { DomEvent, } from '../table';
7
+ import { SpectricInput } from '../input';
8
+ import "./ColorPicker.css"
9
+ export interface ColorPickerProps {
10
+ /**Color in hex*/
11
+ value?: string,
12
+ showAlpha?: boolean
13
+ }
14
+
15
+ //Not all browsers support eyedropper
16
+ interface EyeDropper {
17
+ new(): EyeDropper;
18
+
19
+ /**
20
+ * Opens the eyedropper mode, allowing the user to select a color from the screen.
21
+ * Returns a Promise that resolves with an object containing the selected color in sRGB hexadecimal format.
22
+ * The Promise rejects if the user cancels the eyedropper mode (e.g., by pressing Escape).
23
+ */
24
+ open(): Promise<{ sRGBHex: string }>;
25
+ }
26
+
27
+ declare var EyeDropper: EyeDropper;
28
+ let eyedropper: EyeDropper | undefined
29
+ // if (window.hasOwnProperty("EyeDropper")) {
30
+ // eyedropper = new EyeDropper()
31
+ // }
32
+ @customElement('spectric-colorpicker')
33
+ export class SpectricColorPicker extends LitElement implements ColorPickerProps {
34
+ @property({ type: String, reflect: true })
35
+ value = "#FF0000FF"
36
+ @property({ type: Boolean, reflect: true })
37
+ showAlpha = true
38
+ @state()
39
+ private hue: number = 0;
40
+ @state()
41
+ private alpha: number = 1;
42
+ @state()
43
+ private saturation: number = 1;
44
+ @state()
45
+ private lightness: number = 1;
46
+ private canvas = createRef<HTMLCanvasElement>()
47
+ private original?: string;
48
+ /**is mouse down on saturation/light canvas? */
49
+ private sldown: boolean = false;
50
+
51
+ protected createRenderRoot(): HTMLElement | DocumentFragment {
52
+ return this
53
+ }
54
+
55
+ protected update(changedProperties: PropertyValues): void {
56
+ if (changedProperties.has("value")) {
57
+ let { h, s, l, a } = hexToHsl(this.value)
58
+ this.hue = h
59
+ this.saturation = s;
60
+ this.lightness = l;
61
+ this.alpha = a
62
+ this.renderHueSaturationGrid()
63
+ }
64
+ super.update(changedProperties)
65
+ }
66
+ private renderHueSaturationGrid() {
67
+ if (!this.canvas.value) {
68
+ return
69
+ }
70
+ let canvas: HTMLCanvasElement = this.canvas.value;
71
+ let ctx = canvas.getContext("2d");
72
+ if (!ctx) {
73
+ return
74
+ }
75
+ let xpix = this.canvas.value.width / 100
76
+ let ypix = this.canvas.value.height / 100
77
+ let x = 0,
78
+ y = 0; //pixel size
79
+ for (let m of hslGen(this.hue)) {
80
+ ctx.fillStyle = m;
81
+ ctx.fillRect(x, y, xpix, ypix); //Need to made this a 2d saturation, light graph
82
+ x += xpix;
83
+ x = x % (xpix * 101);
84
+ if (!x)
85
+ y += ypix;
86
+ }
87
+ }
88
+ _handleSaturationLightnessClick = (e: PointerEvent) => {
89
+ if (!this.canvas.value) {
90
+ return
91
+ }
92
+ let { offsetX, offsetY } = e
93
+ this.saturation = offsetX / this.canvas.value.width
94
+ this.lightness = (this.canvas.value.height - offsetY) / this.canvas.value.height
95
+ this.updateValue()
96
+ }
97
+ _handleHueChange = (e: DomEvent<SpectricInput>) => {
98
+ this.hue = (parseInt(String(e.target.value)) / 100) * 360
99
+ e.target.style.setProperty("accent-color", `hsl(${this.hue}deg 100% 50%)`)
100
+ this.renderHueSaturationGrid()
101
+ this.updateValue()
102
+ }
103
+ private updateValue() {
104
+ this.value = hslToHex(this.hue, this.saturation * 100, this.lightness * 100) + (Math.round(this.alpha * 255).toString(16).padStart(2, "0"))
105
+ }
106
+ _handleAlphaChange = (e: DomEvent<SpectricInput>) => {
107
+ this.alpha = parseInt(String(e.target.value)) / 100
108
+ this.updateValue()
109
+ }
110
+ _handleApply = () => {
111
+ this.updateValue()
112
+ this.querySelector<PopoverElement>("spectric-popover")?.hidePopover()
113
+ }
114
+ _cancel = () => {
115
+ if (this.original) {
116
+ this.value = this.original
117
+ }
118
+ this.querySelector<PopoverElement>("spectric-popover")?.hidePopover()
119
+ }
120
+ _openPopover = async () => {
121
+ this.original = this.value
122
+ this.querySelector<PopoverElement>("spectric-popover")?.showPopover()
123
+ while (!this.canvas.value) {
124
+ await new Promise(resolve => setTimeout(resolve, 100))
125
+ }
126
+ this.renderHueSaturationGrid()
127
+ }
128
+ protected getPopover(): TemplateResult {
129
+ return html`
130
+ <spectric-input label="Hue" class="hue-gradient" variant="range" .value=${this.hue || 0} style="accent-color:hsl(${this.hue}deg 100% 50%)" @change=${this._handleHueChange}></spectric-input>
131
+ <canvas ${ref(this.canvas)} width=200 height=100 class="color-picker-saturation-lightness-grid" @click=${this._handleSaturationLightnessClick}
132
+ @mousedown=${() => { this.sldown = true }}
133
+ @mouseup=${() => this.sldown = false}
134
+ @mousemove=${(e: PointerEvent) => { if (this.sldown) { this._handleSaturationLightnessClick(e) } }}
135
+ ></canvas>
136
+ <spectric-input class="alpha-gradient" ?hidden=${!this.showAlpha} label="Opacity" variant="range" .value=${(this.alpha || 1) * 100} @change=${this._handleAlphaChange}></spectric-input>
137
+ <div class="color-picker-footer">
138
+ <spectric-button ?hidden=${eyedropper === undefined} icon class="color-picker-eyedropper" @click=${async () => {
139
+ if (eyedropper) {
140
+ let result = await eyedropper?.open()
141
+ let { h, s, l, a } = hexToHsl(result.sRGBHex);
142
+ this.hue = h
143
+ this.saturation = s
144
+ this.lightness = l
145
+ this.alpha = a
146
+ this.updateValue()
147
+ }
148
+ }}>${eyedropper_icon}</spectric-button>
149
+ <spectric-button @click=${this._handleApply}>Apply</spectric-button>
150
+ <spectric-button variant="secondary" @click=${this._cancel}>Cancel</spectric-button>
151
+ </div>
152
+ `
153
+ }
154
+
155
+ protected render(): unknown {
156
+ return html`
157
+ <spectric-button variant="text" @click=${this._openPopover} icon>
158
+ <spectric-popover .text=${this.getPopover()} icon variant="text"></spectric-popover>
159
+ <div style="width:15px;height:15px;background-color:${this.value}"></div>
160
+ </spectric-button>
161
+ `;
162
+ }
163
+ }
164
+ const eyedropper_icon = html`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" height="15" width="15" fill="currentColor"><path d="M222,67.34a33.81,33.81,0,0,0-10.64-24.25C198.12,30.56,176.68,31,163.54,44.18L142.82,65l-.63-.63a22,22,0,0,0-31.11,0l-9,9a14,14,0,0,0,0,19.81l3.47,3.47L53.14,149.1a37.79,37.79,0,0,0-9.84,36.73l-8.31,19a11.68,11.68,0,0,0,2.46,13A13.91,13.91,0,0,0,47.32,222,14.15,14.15,0,0,0,53,220.82L71,212.92a37.92,37.92,0,0,0,35.84-10.07l52.44-52.46,3.47,3.48a14,14,0,0,0,19.8,0l9-9a22,22,0,0,0,0-31.12l-.66-.66L212,91.85A33.76,33.76,0,0,0,222,67.34Zm-123.61,127a26,26,0,0,1-26,6.47,6,6,0,0,0-4.16.24l-20,8.75a2,2,0,0,1-2.09-.31l9.12-20.9a5.94,5.94,0,0,0,.19-4.31,25.88,25.88,0,0,1,6.26-26.72l52.44-52.45,36.76,36.78Zm105.16-111L178.17,108.9a6,6,0,0,0,0,8.47l4.88,4.89a10,10,0,0,1,0,14.15l-9,9a2,2,0,0,1-2.82,0l-60.69-60.7a2,2,0,0,1,0-2.83l9-9a10,10,0,0,1,14.14,0l4.89,4.89a6,6,0,0,0,4.24,1.75h0a6,6,0,0,0,4.25-1.77L172,52.66c8.58-8.58,22.52-9,31.08-.85a22,22,0,0,1,.44,31.57Z"/></svg>`
165
+ function* hslGen(hue: number) {
166
+ for (let l = 100; l >= 0; l--) {
167
+ for (let s = 0; s <= 100; s++) {
168
+ yield `hsl(${hue}, ${s}%, ${l}%)`;
169
+ }
170
+ }
171
+ }
172
+ function hslToHex(h: number, s: number, l: number) {
173
+ l /= 100;
174
+ const a = s * Math.min(l, 1 - l) / 100;
175
+ const f = (n: number) => {
176
+ const k = (n + h / 30) % 12;
177
+ const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
178
+ return Math.round(255 * color).toString(16).padStart(2, '0'); // convert to Hex and prefix "0" if needed
179
+ };
180
+ return `#${f(0)}${f(8)}${f(4)}`;
181
+ }
182
+ function hexToHsl(hex: string) {
183
+ // Convert hex to RGB
184
+ let r = 0, g = 0, b = 0, a = 255;
185
+ if (hex.length === 4 || hex.length === 5) { // Handle shorthand hex codes like #F0C
186
+ r = parseInt(hex[1] + hex[1], 16);
187
+ g = parseInt(hex[2] + hex[2], 16);
188
+ b = parseInt(hex[3] + hex[3], 16);
189
+ if (hex.length === 5) {
190
+ a = parseInt(hex[4] + hex[4], 16);
191
+ }
192
+ } else if (hex.length === 7 || hex.length === 9) { // Handle full hex codes like #FF00CC
193
+ r = parseInt(hex.substring(1, 3), 16);
194
+ g = parseInt(hex.substring(3, 5), 16);
195
+ b = parseInt(hex.substring(5, 7), 16);
196
+ if (hex.length === 9) {
197
+ a = parseInt(hex.substring(7, 9), 16);
198
+ }
199
+ }
200
+
201
+ // Normalize RGB values to a range of 0-1
202
+ r /= 255;
203
+ g /= 255;
204
+ b /= 255;
205
+ a /= 255
206
+ // Find min and max values among R, G, B
207
+ const max = Math.max(r, g, b);
208
+ const min = Math.min(r, g, b);
209
+
210
+ let h = 0, s = 0, l = (max + min) / 2;
211
+
212
+ if (max === min) {
213
+ // Achromatic (grayscale)
214
+ h = 0;
215
+ s = 0;
216
+ } else {
217
+ const d = max - min;
218
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
219
+
220
+ switch (max) {
221
+ case r:
222
+ h = (g - b) / d + (g < b ? 6 : 0);
223
+ break;
224
+ case g:
225
+ h = (b - r) / d + 2;
226
+ break;
227
+ case b:
228
+ h = (r - g) / d + 4;
229
+ break;
230
+ }
231
+ h /= 6;
232
+ }
233
+
234
+ // Convert Hue to degrees
235
+ h = Math.round(h * 360);
236
+
237
+ //leave sla as 0-1
238
+ return { h, s, l, a };
239
+ }
240
+ export interface ColorPickerEventMap {
241
+ 'change': (event: CustomEvent<ColorPickerProps>) => void
242
+ }
243
+
244
+ declare global {
245
+
246
+ interface HTMLElementTagNameMap {
247
+ "spectric-colorpicker": HTMLElementTagWithEvents<SpectricColorPicker, ColorPickerEventMap>
248
+ }
249
+
250
+ namespace JSX {
251
+ interface IntrinsicElements {
252
+ /**
253
+ * {@link SpectricColorPicker}
254
+ */
255
+ "spectric-colorpicker": ReactElementWithPropsAndEvents<SpectricColorPicker, ColorPickerProps, ColorPickerEventMap>;
256
+ }
257
+ }
258
+ namespace React {
259
+ namespace JSX {
260
+ interface IntrinsicElements {
261
+ /**
262
+ * {@link SpectricColorPicker}
263
+ */
264
+ "spectric-colorpicker": ReactElementWithPropsAndEvents<SpectricColorPicker, ColorPickerProps, ColorPickerEventMap>;
265
+ }
266
+ }
267
+ }
268
+ }
@@ -0,0 +1 @@
1
+ import "./ColorPicker"
@@ -10,4 +10,6 @@ export * from "./dialog"
10
10
  export * from "./splitview"
11
11
  export * from "./pagination"
12
12
  export * from "./table"
13
- export * from "./tooltip"
13
+ export * from "./tooltip"
14
+ export * from "./types"
15
+ export * from "./color_picker"
@@ -14,7 +14,6 @@ spectric-input {
14
14
  spectric-input .inputWrapper {
15
15
  color: var(--text-secondary)
16
16
  }
17
-
18
17
  spectric-input .inputWrapper input {
19
18
  box-sizing: border-box;
20
19
  margin: 0px;
@@ -67,7 +66,9 @@ spectric-input .inputContainer::after {
67
66
  spectric-input #helper-text {
68
67
  height: 18px;
69
68
  }
70
-
69
+ spectric-input #helper-text.hidden {
70
+ display: none;
71
+ }
71
72
  spectric-input[variant="password"] spectric-button {
72
73
  position: absolute;
73
74
  right: 4px;
@@ -77,4 +78,11 @@ spectric-input[variant="password"] spectric-button {
77
78
  spectric-input .checkbox{
78
79
  display: flex;
79
80
  justify-self: center;
81
+ }
82
+ spectric-input spectric-colorpicker{
83
+ display: block;
84
+ }
85
+
86
+ spectric-input[variant="color"] .fieldwrapper{
87
+ display: inline-block;
80
88
  }
@@ -1,9 +1,11 @@
1
1
  import { html, LitElement, PropertyValues, TemplateResult } from 'lit';
2
2
  import "./input.css"
3
- import { customElement, property, query } from 'lit/decorators.js';
3
+ import { customElement, eventOptions, property, query } from 'lit/decorators.js';
4
4
  import { ifDefined } from 'lit/directives/if-defined.js';
5
5
  import { ReactElementWithPropsAndEvents } from './types';
6
6
  import { ButtonSizesTypes } from './Button';
7
+ import { DomEvent } from './table';
8
+ import { SpectricColorPicker } from './color_picker/ColorPicker';
7
9
 
8
10
  export enum InputVariants {
9
11
  Text = 'text',
@@ -17,7 +19,8 @@ export enum InputVariants {
17
19
  file = "file",//display drop area
18
20
  hidden = "hidden",//display drop area
19
21
  password = "password",
20
- checkbox = "checkbox"
22
+ checkbox = "checkbox",
23
+ range = "range"
21
24
 
22
25
  }
23
26
  type InputVariantsTypes = `${InputVariants}`
@@ -232,6 +235,11 @@ export class SpectricInput extends LitElement implements InputProps {
232
235
  this.dispatchEvent(new Event("change", { bubbles: true }))
233
236
  }
234
237
  }
238
+ @eventOptions({ capture: true })
239
+ _handleChange(e: Event) {
240
+ e.stopPropagation()
241
+ this.dispatchEvent(new Event("change", { bubbles: true }))
242
+ }
235
243
  protected render(): unknown {
236
244
  switch (this.variant) {
237
245
  case InputVariants.Text:
@@ -243,7 +251,7 @@ export class SpectricInput extends LitElement implements InputProps {
243
251
  case InputVariants.date://replace with custom date picker
244
252
  case InputVariants.datetime://replace with custom date picker
245
253
  case InputVariants.file://replace with drag and drop location and custom select file button
246
- case InputVariants.color: //replace with custom color picker
254
+ case InputVariants.range:
247
255
  return html`
248
256
  <div class="inputWrapper">
249
257
  <div class="text-input__label-helper-wrapper">
@@ -271,9 +279,7 @@ export class SpectricInput extends LitElement implements InputProps {
271
279
  .value="${this._value as string}"
272
280
  maxlength="${ifNonEmpty(this.maxCount > 0 ? this.maxCount : undefined)}"
273
281
  @input="${this._handleInput}"
274
- @change=${() => {
275
- this.dispatchEvent(new Event("change", { bubbles: true }))
276
- }}
282
+ @change=${this._handleChange}
277
283
  />
278
284
 
279
285
  ${this.variant === String(InputVariants.password) && this.showPasswordVisibilityToggle
@@ -290,6 +296,7 @@ export class SpectricInput extends LitElement implements InputProps {
290
296
  </div>
291
297
  <div
292
298
  id="helper-text"
299
+ class="${this.helperText || this.invalid ? "" : "hidden"}"
293
300
  >
294
301
  <slot name="helper-text"> ${this.invalid ? this.invalidText : this.helperText} </slot>
295
302
  </div>
@@ -298,18 +305,40 @@ export class SpectricInput extends LitElement implements InputProps {
298
305
  </div>
299
306
  `;
300
307
 
308
+ case InputVariants.color: //replace with custom color picker
309
+ return html`<div class="inputWrapper">
310
+ <div class="text-input__label-helper-wrapper">
311
+ <div class="--text-input__label-wrapper">
312
+ ${this.label} ${this.maxCount > 0 && this._value ? `${(this._value as String).length}/${this.maxCount}` : null}
313
+ </div>
314
+ </div>
315
+ <div class="fieldwrapper">
316
+ <div ?data-invalid="${this.invalid}" class="inputContainer">
317
+ <spectric-colorpicker @change=${(e: DomEvent<SpectricColorPicker>) => { this.value = e.target.value; this._handleChange(e) }}></spectric-colorpicker>
318
+ </div>
319
+ <div
320
+ id="helper-text"
321
+ class="${this.helperText || this.invalid ? "" : "hidden"}"
322
+ >
323
+ <slot name="helper-text"> ${this.invalid ? this.invalidText : this.helperText} </slot>
324
+ </div>
325
+
326
+ </div>
327
+ </div>`
301
328
  case InputVariants.hidden:
302
- return html`<input type="hidden"></input>`
329
+ return html`<input type="hidden"/>`
303
330
 
304
331
  case InputVariants.checkbox:
305
332
 
306
333
  return html`
307
334
  <div class="checkbox">
308
- <spectric-button @click=${() => {
335
+ <spectric-button
336
+ .tooltip=${this.helperText}
337
+ @click=${() => {
309
338
  this.checked = !this.checked;
310
339
  this.value = Boolean(this.checked);
311
340
  }} icon size=${this.size || "xxsmall"} variant=${this.checked ? "primary" : "secondary"}>${this.checked ? '✓' : "\u00A0"}</spectric-button>
312
- ${this.invalid || this.helperText ? html`<spectric-tooltip text=${this.invalid || this.helperText}></spectric-tooltip>` : null}
341
+
313
342
  ${this.label}
314
343
  </div>
315
344
  </label>
@@ -18,7 +18,6 @@ interface PaginationProps extends PaginationChangeProps {
18
18
  pageSizeOptions?: number[]
19
19
  }
20
20
 
21
- console.log("Pagination")
22
21
 
23
22
  /**
24
23
  * Pagination Element
@@ -47,7 +46,7 @@ export class PaginationElement extends LitElement implements PaginationProps {
47
46
  protected createRenderRoot(): HTMLElement | DocumentFragment {
48
47
  return this
49
48
  }
50
- protected updated(_changedProperties: PropertyValues): void {
49
+ protected update(_changedProperties: PropertyValues): void {
51
50
  if (_changedProperties.has("pageSize") && !this.pageSizeOptions.includes(this.pageSize)) {
52
51
  this.pageSizeOptions = [...this.pageSizeOptions, this.pageSize].sort((a, b) => a - b)
53
52
  /**
@@ -60,6 +59,7 @@ export class PaginationElement extends LitElement implements PaginationProps {
60
59
  }
61
60
  })
62
61
  }
62
+ super.update(_changedProperties)
63
63
  }
64
64
  private _handlePageUp = () => {
65
65
  this.page += 1