jsl-ui 9.28.7 → 10.0.0

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 (166) hide show
  1. package/docs/AutoSuggestComponent.md +17 -0
  2. package/docs/ButtonComponent.md +14 -0
  3. package/docs/CheckBoxComponent.md +12 -0
  4. package/docs/DateComponent.md +12 -0
  5. package/docs/DateRangeComponent.md +13 -0
  6. package/docs/DateTimeComponent.md +13 -0
  7. package/docs/DialogComponent.md +15 -0
  8. package/docs/ExpanderComponent.md +13 -0
  9. package/docs/FormComponent.md +15 -0
  10. package/docs/FormFieldComponent.md +15 -0
  11. package/docs/GridComponent.md +19 -0
  12. package/docs/InputComponent.md +15 -0
  13. package/docs/IntervalComponent.md +13 -0
  14. package/docs/LoadingComponent.md +12 -0
  15. package/docs/NavigationComponent.md +17 -0
  16. package/docs/Notification.md +14 -0
  17. package/docs/NotificationComponent.md +13 -0
  18. package/docs/NumberComponent.md +12 -0
  19. package/docs/OverlayComponent.md +12 -0
  20. package/docs/ProgressComponent.md +12 -0
  21. package/docs/README.md +24 -0
  22. package/docs/RibbonMenuComponent.md +14 -0
  23. package/docs/SelectComponent.md +15 -0
  24. package/docs/SplitterComponent.md +14 -0
  25. package/docs/StackPanelComponent.md +13 -0
  26. package/docs/TabsComponent.md +16 -0
  27. package/docs/TimeComponent.md +13 -0
  28. package/docs/TreeViewComponent.md +16 -0
  29. package/examples/showcase/checks-and-expanders.ts +163 -0
  30. package/examples/showcase/cultures.ts +39 -0
  31. package/examples/showcase/dates.ts +182 -0
  32. package/examples/showcase/dialogs.ts +184 -0
  33. package/examples/showcase/feedback-and-navigation.ts +174 -0
  34. package/examples/showcase/fields.ts +178 -0
  35. package/examples/showcase/fonts/Jost-Variable.ttf +0 -0
  36. package/examples/showcase/fonts/OFL-Jost.txt +93 -0
  37. package/examples/showcase/grids.ts +150 -0
  38. package/examples/showcase/index.html +60 -0
  39. package/examples/showcase/layout-and-navigation.ts +47 -0
  40. package/examples/showcase/nivona.css +98 -0
  41. package/examples/showcase/selects.ts +158 -0
  42. package/examples/showcase/showcase.css +231 -0
  43. package/examples/showcase/showcase.ts +214 -0
  44. package/examples/showcase/tabs.ts +161 -0
  45. package/examples/showcase/themes.css +83 -0
  46. package/examples/showcase/themes.ts +54 -0
  47. package/lib/components/IntervalComponent.d.ts +5 -0
  48. package/lib/components/IntervalComponent.js +29 -13
  49. package/lib/components/auto-suggest/AutoSuggestComponent.d.ts +11 -9
  50. package/lib/components/auto-suggest/AutoSuggestComponent.js +25 -38
  51. package/lib/components/button/ButtonComponent.d.ts +30 -1
  52. package/lib/components/button/ButtonComponent.js +85 -4
  53. package/lib/components/checkbox/CheckBoxComponent.d.ts +11 -1
  54. package/lib/components/checkbox/CheckBoxComponent.js +66 -56
  55. package/lib/components/checkbox/CheckBoxStyle.scss +72 -85
  56. package/lib/components/date/DateComponent.d.ts +10 -0
  57. package/lib/components/date/DateComponent.js +92 -44
  58. package/lib/components/date-range/DateRangeComponent.d.ts +14 -3
  59. package/lib/components/date-range/DateRangeComponent.js +96 -86
  60. package/lib/components/date-range/DateRangeStyle.scss +35 -11
  61. package/lib/components/date-time/DateTimeComponent.d.ts +8 -0
  62. package/lib/components/date-time/DateTimeComponent.js +64 -23
  63. package/lib/components/date-time/DateTimeStyle.scss +10 -7
  64. package/lib/components/dialog/DialogComponent.d.ts +21 -7
  65. package/lib/components/dialog/DialogComponent.js +179 -122
  66. package/lib/components/expander/ExpanderComponent.d.ts +15 -1
  67. package/lib/components/expander/ExpanderComponent.js +85 -64
  68. package/lib/components/expander/ExpanderStyle.scss +61 -47
  69. package/lib/components/form-field/FormFieldComponent.d.ts +45 -15
  70. package/lib/components/form-field/FormFieldComponent.js +249 -96
  71. package/lib/components/form-field/FormFieldStyle.scss +36 -87
  72. package/lib/components/form-field/create-control.d.ts +7 -0
  73. package/lib/components/form-field/create-control.js +48 -0
  74. package/lib/components/input/InputComponent.d.ts +18 -1
  75. package/lib/components/input/InputComponent.js +68 -93
  76. package/lib/components/loading/LoadingComponent.d.ts +9 -2
  77. package/lib/components/loading/LoadingComponent.js +13 -12
  78. package/lib/components/loading/LoadingStyle.css +19 -29
  79. package/lib/components/navigation/NavigationComponent.d.ts +34 -10
  80. package/lib/components/navigation/NavigationComponent.js +261 -33
  81. package/lib/components/navigation/NavigationStyle.scss +62 -226
  82. package/lib/components/notification/Notification.d.ts +37 -4
  83. package/lib/components/notification/Notification.js +160 -41
  84. package/lib/components/notification/Notification.scss +103 -19
  85. package/lib/components/number/NumberComponent.d.ts +16 -2
  86. package/lib/components/number/NumberComponent.js +54 -53
  87. package/lib/components/overlay/OverlayComponent.d.ts +13 -7
  88. package/lib/components/overlay/OverlayComponent.js +46 -114
  89. package/lib/components/overlay/overlay.css +18 -57
  90. package/lib/components/progress/ProgressComponent.d.ts +1 -0
  91. package/lib/components/progress/ProgressComponent.js +6 -6
  92. package/lib/components/progress/ProgressStyle.scss +5 -15
  93. package/lib/components/ribbon-menu/RibbonMenuComponent.js +2 -2
  94. package/lib/components/ribbon-menu/RibbonMenuStyle.scss +6 -76
  95. package/lib/components/select/SelectComponent.d.ts +33 -3
  96. package/lib/components/select/SelectComponent.js +312 -278
  97. package/lib/components/select/SelectDDAreaComponent.d.ts +13 -7
  98. package/lib/components/select/SelectDDAreaComponent.js +215 -256
  99. package/lib/components/select/SelectStyle.scss +204 -186
  100. package/lib/components/slider/nouislider.css +40 -54
  101. package/lib/components/stack-panel/SplitterComponent.d.ts +19 -3
  102. package/lib/components/stack-panel/SplitterComponent.js +94 -111
  103. package/lib/components/stack-panel/SplitterStyle.scss +9 -45
  104. package/lib/components/stack-panel/StackPanelComponent.js +3 -3
  105. package/lib/components/stack-panel/StackPanelStyle.scss +10 -52
  106. package/lib/components/tabs/TabsComponent.d.ts +17 -1
  107. package/lib/components/tabs/TabsComponent.js +247 -123
  108. package/lib/components/tabs/TabsStyle.scss +138 -75
  109. package/lib/components/time/TimeComponent.d.ts +5 -0
  110. package/lib/components/time/TimeComponent.js +61 -69
  111. package/lib/components/time/TimeComponent.scss +4 -4
  112. package/lib/components/tree-view/TreeViewComponent.d.ts +10 -4
  113. package/lib/components/tree-view/TreeViewComponent.js +65 -32
  114. package/lib/components/tree-view/TreeViewStyle.scss +12 -96
  115. package/lib/core/date.d.ts +2 -0
  116. package/lib/core/date.js +6 -0
  117. package/lib/core/jq.js +11 -8
  118. package/lib/grid/slick-default-theme.css +107 -31
  119. package/lib/grid/slick.creator.js +23 -5
  120. package/lib/grid/slick.formatters.js +4 -3
  121. package/lib/grid/slick.grid.css +38 -26
  122. package/lib/grid/slick.grid.d.ts +1 -0
  123. package/lib/grid/slick.grid.js +2 -3
  124. package/lib/grid.css +3413 -454
  125. package/lib/index.css +2500 -1031
  126. package/lib/index.css.map +1 -1
  127. package/lib/index.d.ts +1 -0
  128. package/lib/index.js +1 -1
  129. package/lib/index.js.map +1 -1
  130. package/lib/interaction/moveable.d.ts +1 -0
  131. package/lib/interaction/moveable.js +8 -0
  132. package/lib/interaction/resizable.d.ts +1 -0
  133. package/lib/interaction/resizable.js +9 -0
  134. package/lib/ui/auto.suggest.css +31 -14
  135. package/lib/ui/auto.suggest.d.ts +1 -0
  136. package/lib/ui/auto.suggest.js +88 -18
  137. package/lib/ui/base.css +2 -48
  138. package/lib/ui/button.css +134 -0
  139. package/lib/ui/date-input.css +11 -0
  140. package/lib/ui/date.picker/css/tiny-date-picker.css +124 -325
  141. package/lib/ui/date.picker/date-picker-options.js +4 -3
  142. package/lib/ui/date.picker/lib/date-manip.js +9 -0
  143. package/lib/ui/date.picker/mode/base-mode.d.ts +2 -1
  144. package/lib/ui/date.picker/mode/base-mode.js +112 -22
  145. package/lib/ui/date.picker/mode/dropdown-mode.d.ts +2 -4
  146. package/lib/ui/date.picker/mode/dropdown-mode.js +29 -42
  147. package/lib/ui/date.picker/mode/index.d.ts +2 -1
  148. package/lib/ui/date.picker/mode/modal-mode.d.ts +2 -1
  149. package/lib/ui/date.picker/mode/modal-mode.js +4 -2
  150. package/lib/ui/date.picker/mode/permanent-mode.d.ts +2 -1
  151. package/lib/ui/date.picker/views/day-picker.js +24 -15
  152. package/lib/ui/date.picker/views/month-picker.js +1 -1
  153. package/lib/ui/date.picker/views/year-picker.js +3 -2
  154. package/lib/ui/date.picker.d.ts +2 -1
  155. package/lib/ui/dialog.css +144 -108
  156. package/lib/ui/dialog.d.ts +19 -1
  157. package/lib/ui/dialog.js +178 -60
  158. package/lib/ui/input.css +35 -0
  159. package/lib/ui/number.input.js +50 -37
  160. package/lib/ui/theme.css +28 -0
  161. package/package.json +12 -8
  162. package/lib/components/drop-down/DropDownComponent.d.ts +0 -27
  163. package/lib/components/drop-down/DropDownComponent.js +0 -60
  164. package/lib/components/expander/OldExpanderStyle.scss +0 -37
  165. package/lib/ui/drop.down.d.ts +0 -52
  166. package/lib/ui/drop.down.js +0 -2112
@@ -1,65 +1,184 @@
1
1
  import "./Notification.scss";
2
2
  import { JSLRender } from "jsl-render";
3
3
  import { helper } from "../../core/helper";
4
- export class Notification {
5
- static show(options) {
6
- if (Notification.container == null) {
7
- Notification.createContainer();
4
+ /** The same surface can be rendered inline or hosted by Notification.show(). */
5
+ export class NotificationComponent {
6
+ constructor(options) {
7
+ this.options = options;
8
+ this.State = { closed: false };
9
+ this.id = helper.newDomGuid();
10
+ this.interactions = new Set();
11
+ }
12
+ onInit(renderer) { this.renderer = renderer; }
13
+ onCreate(node) { this.dom = node.dom; this.updateProgress(); }
14
+ onRemove() { var _a; (_a = this.progressAnimation) === null || _a === void 0 ? void 0 : _a.cancel(); this.dom = undefined; }
15
+ /** The notification host keeps the visual countdown synchronized with dismissal. */
16
+ setCountdown(remaining, paused) {
17
+ this.countdown = { remaining, paused, updatedAt: Date.now() };
18
+ this.updateProgress();
19
+ }
20
+ updateProgress() {
21
+ var _a, _b;
22
+ (_a = this.progressAnimation) === null || _a === void 0 ? void 0 : _a.cancel();
23
+ const bar = (_b = this.dom) === null || _b === void 0 ? void 0 : _b.querySelector(".jsl-notification-progress-fill");
24
+ if (!bar || !this.countdown) {
25
+ return;
26
+ }
27
+ const remaining = Math.max(0, this.countdown.remaining - (this.countdown.paused ? 0 : Date.now() - this.countdown.updatedAt));
28
+ const fraction = remaining / this.options.timeOut;
29
+ bar.style.transform = "scaleX(" + fraction + ")";
30
+ if (!this.countdown.paused && remaining > 0) {
31
+ this.progressAnimation = bar.animate([
32
+ { transform: "scaleX(" + fraction + ")" }, { transform: "scaleX(0)" }
33
+ ], { duration: remaining, fill: "forwards", easing: "linear" });
8
34
  }
9
- const id = helper.newDomGuid();
10
- // add
11
- const notification = {
12
- tag: "div",
13
- attr: { id, style: options.style, class: "jsl-notification " + (options.class || "") },
14
- animation: { attr: "opacity", from: 0, to: 1, duration: 600 },
15
- children: [
16
- typeof options.content === "string" ? { tag: "div", content: options.content } : options.content
35
+ }
36
+ render() {
37
+ var _a, _b, _c;
38
+ const variant = (_a = this.options.variant) !== null && _a !== void 0 ? _a : "default";
39
+ const symbols = { default: "i", info: "i", success: "✓", warning: "!", error: "!" };
40
+ return {
41
+ tag: "div", attr: {
42
+ id: this.id, hidden: this.State.closed ? "hidden" : undefined,
43
+ class: "jsl-notification jsl-notification--" + variant + " " + ((_b = this.options.class) !== null && _b !== void 0 ? _b : ""),
44
+ style: this.options.style, role: variant === "error" || variant === "warning" ? "alert" : "status",
45
+ "aria-atomic": "true",
46
+ tabindex: this.options.onClick ? "0" : undefined,
47
+ click: () => { this.activate(); return false; },
48
+ keydown: (event) => {
49
+ if (event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) {
50
+ event.preventDefault();
51
+ event.stopPropagation();
52
+ this.activate();
53
+ }
54
+ return false;
55
+ },
56
+ mouseenter: () => { this.pause("pointer", true); return false; },
57
+ mouseleave: () => { this.pause("pointer", false); return false; },
58
+ focusin: () => { this.pause("focus", true); return false; },
59
+ focusout: (event) => {
60
+ if (!event.currentTarget.contains(event.relatedTarget)) {
61
+ this.pause("focus", false);
62
+ }
63
+ return false;
64
+ }
65
+ }, children: [
66
+ { tag: "span", content: symbols[variant], attr: { class: "jsl-notification-icon", "aria-hidden": "true" } },
67
+ { tag: "div", attr: { class: "jsl-notification-copy" }, children: [
68
+ { tag: "div", content: this.options.title, attr: { class: "jsl-notification-title", hidden: this.options.title ? undefined : "hidden" } },
69
+ typeof this.options.content === "string" ? { tag: "div", content: this.options.content } : this.options.content
70
+ ] },
71
+ { tag: "button", attr: {
72
+ type: "button", class: "jsl-notification-close",
73
+ hidden: this.options.showCloseButton === false ? "hidden" : undefined,
74
+ "aria-label": (_c = this.options.closeButtonLabel) !== null && _c !== void 0 ? _c : "Dismiss notification",
75
+ click: (event) => { event.stopPropagation(); this.dismiss(); return false; }
76
+ }, children: [{ tag: "span", content: "×", attr: { "aria-hidden": "true" } }] },
77
+ ...(this.countdown ? [{ tag: "div", attr: { class: "jsl-notification-progress", "aria-hidden": "true" }, children: [
78
+ { tag: "span", attr: { class: "jsl-notification-progress-fill" } }
79
+ ] }] : [])
17
80
  ]
18
81
  };
19
- Notification.containerVNode.children.push(notification);
20
- Notification.renderer.refresh();
21
- // remove
22
- if (options.timeOut) {
23
- setTimeout(() => {
24
- const idx = Notification.containerVNode.children.indexOf(notification);
25
- if (idx >= 0) {
26
- Notification.containerVNode.children.splice(idx, 1);
27
- }
28
- Notification.renderer.refresh();
29
- }, options.timeOut);
82
+ }
83
+ dismiss() {
84
+ var _a, _b, _c;
85
+ if (this.State.closed) {
86
+ return;
87
+ }
88
+ this.State.closed = true;
89
+ (_b = (_a = this.options).onClose) === null || _b === void 0 ? void 0 : _b.call(_a);
90
+ (_c = this.renderer) === null || _c === void 0 ? void 0 : _c.refresh();
91
+ }
92
+ activate() {
93
+ var _a, _b, _c, _d;
94
+ (_b = (_a = this.options).onClick) === null || _b === void 0 ? void 0 : _b.call(_a);
95
+ if ((_c = this.options.closeOnClick) !== null && _c !== void 0 ? _c : true) {
96
+ this.dismiss();
97
+ }
98
+ else {
99
+ (_d = this.renderer) === null || _d === void 0 ? void 0 : _d.refresh();
100
+ }
101
+ }
102
+ pause(reason, paused) {
103
+ var _a;
104
+ const wasPaused = this.interactions.size > 0;
105
+ if (paused) {
106
+ this.interactions.add(reason);
30
107
  }
31
- notification.attr.click = () => {
32
- var _a;
33
- if (options.onClick != null) {
34
- options.onClick();
108
+ else {
109
+ this.interactions.delete(reason);
110
+ }
111
+ if (wasPaused !== (this.interactions.size > 0)) {
112
+ (_a = this.onInteractionChange) === null || _a === void 0 ? void 0 : _a.call(this, this.interactions.size > 0);
113
+ }
114
+ }
115
+ }
116
+ export class Notification {
117
+ static show(options) {
118
+ var _a, _b;
119
+ if (!((_a = Notification.container) === null || _a === void 0 ? void 0 : _a.isConnected)) {
120
+ Notification.createContainer();
121
+ }
122
+ const component = new NotificationComponent({
123
+ ...options, onClose: () => {
124
+ var _a;
125
+ Notification.remove(component.id);
126
+ (_a = options.onClose) === null || _a === void 0 ? void 0 : _a.call(options);
35
127
  }
36
- if (((_a = options.closeOnClick) !== null && _a !== void 0 ? _a : true) === true) {
37
- const idx = Notification.containerVNode.children.indexOf(notification);
38
- if (idx >= 0) {
39
- Notification.containerVNode.children.splice(idx, 1);
128
+ });
129
+ const entry = { component, remaining: Math.max(0, (_b = options.timeOut) !== null && _b !== void 0 ? _b : 0), started: 0 };
130
+ Notification.entries.set(component.id, entry);
131
+ component.onInteractionChange = paused => {
132
+ if (!Notification.entries.has(component.id) || !entry.remaining) {
133
+ return;
134
+ }
135
+ if (paused) {
136
+ if (entry.timer != null) {
137
+ clearTimeout(entry.timer);
138
+ entry.timer = undefined;
139
+ entry.remaining = Math.max(1, entry.remaining - (Date.now() - entry.started));
140
+ component.setCountdown(entry.remaining, true);
40
141
  }
41
142
  }
143
+ else {
144
+ Notification.schedule(entry);
145
+ }
42
146
  };
43
- return id;
44
- }
45
- static clearNotification(id) {
46
- Notification.containerVNode.children = Notification.containerVNode.children.filter((x) => { var _a; return ((_a = x.attr) === null || _a === void 0 ? void 0 : _a.id) !== id; });
147
+ Notification.containerVNode.children.push(component);
47
148
  Notification.renderer.refresh();
149
+ Notification.schedule(entry);
150
+ return component.id;
48
151
  }
49
- static clearAll() {
50
- if (Notification.containerVNode != null) {
51
- Notification.containerVNode.children = [];
152
+ static clearNotification(id) { var _a; (_a = Notification.entries.get(id)) === null || _a === void 0 ? void 0 : _a.component.dismiss(); }
153
+ static clearAll() { [...Notification.entries.values()].forEach(entry => entry.component.dismiss()); }
154
+ static schedule(entry) {
155
+ if (entry.timer != null || entry.remaining <= 0) {
156
+ return;
52
157
  }
53
- if (Notification.renderer != null) {
54
- Notification.renderer.refresh();
158
+ entry.started = Date.now();
159
+ entry.component.setCountdown(entry.remaining, false);
160
+ entry.timer = setTimeout(() => { entry.timer = undefined; entry.component.dismiss(); }, entry.remaining);
161
+ }
162
+ static remove(id) {
163
+ const entry = Notification.entries.get(id);
164
+ if (!entry) {
165
+ return;
55
166
  }
167
+ clearTimeout(entry.timer);
168
+ Notification.entries.delete(id);
169
+ Notification.containerVNode.children = Notification.containerVNode.children.filter(child => child !== entry.component);
170
+ Notification.renderer.refresh();
56
171
  }
57
172
  static createContainer() {
173
+ // An application may replace its body while notifications exist.
174
+ [...Notification.entries.values()].forEach(entry => clearTimeout(entry.timer));
175
+ Notification.entries.clear();
58
176
  Notification.container = document.createElement("div");
59
- document.body.appendChild(Notification.container);
60
177
  Notification.container.className = "jsl-notification-container";
178
+ document.body.appendChild(Notification.container);
61
179
  Notification.containerVNode = { tag: "div", children: [] };
62
180
  Notification.renderer = new JSLRender(Notification.container, false);
63
181
  Notification.renderer.render(Notification.containerVNode);
64
182
  }
65
183
  }
184
+ Notification.entries = new Map();
@@ -1,19 +1,103 @@
1
- .jsl-notification-container {
2
- position: fixed;
3
- top: 1px;
4
- right: 1px;
5
- overflow: visible;
6
- z-index: 3020;
7
-
8
- .jsl-notification {
9
- box-sizing: border-box;
10
- padding: 0.5rem;
11
- min-width: 12rem;
12
- border-radius: 0.3rem;
13
- // border: 1px solid silver;
14
- color: white;
15
- background: #3B99FF;
16
- margin: 0.2rem;
17
- cursor: pointer;
18
- }
19
- }
1
+ @import "../../ui/theme.css";
2
+
3
+ .jsl-notification-container {
4
+ position: fixed;
5
+ top: 0;
6
+ right: 0;
7
+ width: 25rem;
8
+ max-width: 100vw;
9
+ max-height: 100dvh;
10
+ overflow-y: auto;
11
+ z-index: 3020;
12
+ // Leave space for individual shadows so scrolling doesn't crop them into a rectangle.
13
+ box-sizing: border-box;
14
+ padding: 1rem;
15
+ background: transparent;
16
+ border: 0;
17
+ border-radius: 1.75rem;
18
+ box-shadow: none;
19
+ pointer-events: none;
20
+ scrollbar-width: thin;
21
+ }
22
+ .jsl-notification-container > div { display: flex; flex-direction: column; gap: .75rem; }
23
+ .jsl-notification {
24
+ position: relative;
25
+ --jsl-notification-tone: var(--jsl-color-text-secondary);
26
+ display: grid;
27
+ grid-template-columns: 1.25rem minmax(0, 1fr) auto;
28
+ align-items: start;
29
+ gap: .75rem;
30
+ box-sizing: border-box;
31
+ width: 100%;
32
+ padding: 1rem;
33
+ border: 1px solid var(--jsl-color-border);
34
+ border-radius: .75rem;
35
+ background: var(--jsl-color-surface);
36
+ color: var(--jsl-color-text);
37
+ font: inherit;
38
+ font-size: .8125rem;
39
+ line-height: 1.5;
40
+ box-shadow: 0 8px 24px rgba(0,0,0,.1), 0 1px 4px rgba(0,0,0,.04);
41
+ pointer-events: auto;
42
+ animation: jsl-notification-appear .16s ease-out;
43
+ }
44
+ .jsl-notification[hidden] { display: none; }
45
+ .jsl-notification--info { --jsl-notification-tone: var(--jsl-color-info); }
46
+ .jsl-notification--success { --jsl-notification-tone: var(--jsl-color-success); }
47
+ .jsl-notification--warning { --jsl-notification-tone: var(--jsl-color-warning); }
48
+ .jsl-notification--error { --jsl-notification-tone: var(--jsl-color-danger); }
49
+ .jsl-notification-icon {
50
+ display: grid;
51
+ place-items: center;
52
+ box-sizing: border-box;
53
+ width: 1.125rem;
54
+ height: 1.125rem;
55
+ margin-top: .125rem;
56
+ border: 1.5px solid currentColor;
57
+ border-radius: 50%;
58
+ color: var(--jsl-notification-tone);
59
+ font-size: .6875rem;
60
+ font-weight: 700;
61
+ line-height: 1;
62
+ }
63
+ .jsl-notification--success .jsl-notification-icon { font-size: .75rem; }
64
+ .jsl-notification-copy { min-width: 0; overflow-wrap: anywhere; }
65
+ .jsl-notification-title { margin-bottom: .25rem; font-weight: 600; color: var(--jsl-color-text); }
66
+ .jsl-notification-title[hidden] { display: none; }
67
+ .jsl-notification-close {
68
+ appearance: none;
69
+ display: grid;
70
+ place-items: center;
71
+ width: 1.5rem;
72
+ height: 1.5rem;
73
+ margin: -.125rem -.25rem 0 0;
74
+ padding: 0;
75
+ border: 0;
76
+ border-radius: .25rem;
77
+ background: transparent;
78
+ color: var(--jsl-color-text-muted);
79
+ font: inherit;
80
+ font-size: 1.125rem;
81
+ line-height: 1;
82
+ cursor: pointer;
83
+ }
84
+ .jsl-notification-close[hidden] { display: none; }
85
+ .jsl-notification-progress {
86
+ position: absolute;
87
+ inset: auto 1rem .375rem;
88
+ height: 3px;
89
+ border-radius: 999px;
90
+ overflow: hidden;
91
+ background: var(--jsl-color-border-subtle);
92
+ pointer-events: none;
93
+ }
94
+ .jsl-notification-progress-fill {
95
+ display: block;
96
+ height: 100%;
97
+ background: var(--jsl-color-accent);
98
+ transform-origin: left;
99
+ }
100
+ .jsl-notification-close:hover { background: var(--jsl-color-surface-hover); color: var(--jsl-color-text); }
101
+ .jsl-notification-close:focus-visible, .jsl-notification:focus-visible { outline: 2px solid var(--jsl-color-focus); outline-offset: 2px; }
102
+ @keyframes jsl-notification-appear { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
103
+ @media (prefers-reduced-motion: reduce) { .jsl-notification { animation: none; } }
@@ -1,4 +1,5 @@
1
- import { IJSLComponent, IJSLVNode } from "jsl-render";
1
+ import "../../ui/input.css";
2
+ import { IJSLComponent, IJSLVNode, JSLRender } from "jsl-render";
2
3
  import { INumberInputOptions } from "../../ui/number.input";
3
4
  import { EventHandler } from "../../interaction/eventhandler";
4
5
  import { IDataConverter } from "../../interaction/dataconverter";
@@ -6,21 +7,34 @@ export interface INumberComponentOptions<T = any> extends INumberInputOptions {
6
7
  id?: string;
7
8
  class?: string;
8
9
  style?: string;
9
- placeholder?: string;
10
+ placeholder?: boolean | string;
10
11
  bindTo?: T;
11
12
  bindMode?: "toModel" | "fromModel" | "both";
12
13
  property?: keyof T;
13
14
  converter?: IDataConverter<any, number>;
15
+ unstyled?: boolean;
16
+ ariaLabel?: string;
17
+ readOnly?: boolean;
14
18
  }
15
19
  export declare class NumberComponent<T = any> implements IJSLComponent {
16
20
  private currentDom;
17
21
  private id;
18
22
  private options;
23
+ private modelValue;
24
+ private renderer;
19
25
  constructor(options: INumberComponentOptions<T>);
20
26
  get onChange(): EventHandler<number>;
21
27
  render(): IJSLVNode;
22
28
  getValue(): string;
23
29
  getValueAsNumber(): number;
30
+ isValid(): boolean;
31
+ /** Updates the input and writable binding without raising onChange. */
24
32
  setValue(value: number): void;
33
+ focus(): void;
34
+ onInit(renderer: JSLRender): void;
25
35
  onCreate(node: IJSLVNode): void;
36
+ onRemove(): void;
37
+ private hasBinding;
38
+ private readModel;
39
+ private writeModel;
26
40
  }
@@ -1,80 +1,81 @@
1
+ import "../../ui/input.css";
1
2
  import { NumberInput } from "../../ui/number.input";
2
3
  import { numeric } from "../../core/numeric";
3
4
  import { helper } from "../../core/helper";
4
5
  import { EventHandler } from "../../interaction/eventhandler";
5
6
  export class NumberComponent {
6
7
  constructor(options) {
7
- var _a;
8
- this.options = { ...{ onChange: new EventHandler(), bindMode: "toModel" }, ...options };
9
- if (this.options.bindTo != null && this.options.property != null && this.options.value == null) {
10
- this.options.value = (((_a = this.options.converter) === null || _a === void 0 ? void 0 : _a.toUI) ?
11
- this.options.converter.toUI(this.options.bindTo[this.options.property]) :
12
- this.options.bindTo[this.options.property]);
8
+ this.options = { enabled: true, format: "0.###", bindMode: "toModel", ...options };
9
+ if (this.hasBinding() && this.options.value == null) {
10
+ this.options.value = this.readModel();
13
11
  }
12
+ this.modelValue = this.options.value;
14
13
  this.options.onChange = EventHandler.Create(this.options.onChange);
15
14
  this.id = options.id || helper.newDomGuid();
16
15
  }
17
- get onChange() {
18
- return this.options.onChange;
19
- }
16
+ get onChange() { return this.options.onChange; }
20
17
  render() {
21
- var _a;
22
- let val = this.options.value;
23
- if (this.options.bindMode !== "toModel" && this.options.bindTo != null && this.options.property != null) {
24
- const current = this.getValue();
25
- const newVal = (((_a = this.options.converter) === null || _a === void 0 ? void 0 : _a.toUI) ?
26
- this.options.converter.toUI(this.options.bindTo[this.options.property]) :
27
- this.options.bindTo[this.options.property]);
28
- const newValStr = numeric.format(newVal, this.options.format);
29
- val = newVal;
30
- if (newValStr !== current) {
31
- if (this.currentDom != null) {
32
- this.currentDom.value = newValStr;
18
+ var _a, _b;
19
+ if (this.options.bindMode !== "toModel" && this.hasBinding()) {
20
+ const value = this.readModel();
21
+ if (!Object.is(value, this.modelValue)) {
22
+ this.options.value = value;
23
+ this.modelValue = value;
24
+ if (this.currentDom) {
25
+ this.currentDom.value = numeric.format(value, this.options.format);
33
26
  }
34
27
  }
35
28
  }
36
29
  return {
37
- tag: "input",
38
- attr: {
39
- id: this.id,
40
- class: this.options.class,
41
- style: this.options.style,
42
- value: numeric.format(val, this.options.format),
43
- placeholder: this.options.placeholder
30
+ tag: "input", attr: {
31
+ id: this.id, type: "text", inputmode: (_a = this.options.inputmode) !== null && _a !== void 0 ? _a : "decimal",
32
+ class: (this.options.unstyled ? "" : "jsl-input ") + "jsl-number " + ((_b = this.options.class) !== null && _b !== void 0 ? _b : ""),
33
+ style: this.options.style, value: numeric.format(this.options.value, this.options.format),
34
+ placeholder: typeof this.options.placeholder === "string" ? this.options.placeholder :
35
+ this.options.placeholder === true ? numeric.format(0, this.options.format) : undefined,
36
+ disabled: this.options.enabled ? undefined : "disabled",
37
+ readonly: this.options.readOnly ? "readonly" : undefined,
38
+ "aria-label": this.options.ariaLabel
44
39
  }
45
40
  };
46
41
  }
47
- getValue() {
48
- if (this.currentDom == null) {
49
- return null;
50
- }
51
- return this.currentDom.value;
52
- }
53
- getValueAsNumber() {
54
- return numeric.parse(this.getValue());
55
- }
42
+ getValue() { var _a, _b; return (_b = (_a = this.currentDom) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : numeric.format(this.options.value, this.options.format); }
43
+ getValueAsNumber() { return numeric.parse(this.getValue()); }
44
+ isValid() { return !this.getValue().trim() || Number.isFinite(this.getValueAsNumber()); }
45
+ /** Updates the input and writable binding without raising onChange. */
56
46
  setValue(value) {
57
- var _a;
58
47
  this.options.value = value;
59
- if (this.currentDom != null) {
48
+ if (this.currentDom) {
60
49
  this.currentDom.value = numeric.format(value, this.options.format);
50
+ this.currentDom.setCustomValidity("");
51
+ this.currentDom.removeAttribute("aria-invalid");
61
52
  }
62
- if (this.options.bindMode !== "fromModel" && this.options.bindTo && this.options.property) {
63
- this.options.bindTo[this.options.property] =
64
- this.options.bindTo[this.options.property] = ((_a = this.options.converter) === null || _a === void 0 ? void 0 : _a.toData) ? this.options.converter.toData(value) : value;
65
- }
53
+ this.writeModel(value);
66
54
  }
55
+ focus() { var _a; (_a = this.currentDom) === null || _a === void 0 ? void 0 : _a.focus(); }
56
+ onInit(renderer) { this.renderer = renderer; }
67
57
  onCreate(node) {
68
58
  this.currentDom = node.dom;
69
- const handler = new EventHandler((value) => {
70
- var _a;
71
- if (this.options.bindTo != null && this.options.bindMode !== "fromModel" && this.options.property != null) {
72
- if (this.options.bindTo && this.options.property) {
73
- this.options.bindTo[this.options.property] = ((_a = this.options.converter) === null || _a === void 0 ? void 0 : _a.toData) ? this.options.converter.toData(value) : value;
74
- }
75
- }
76
- this.options.onChange.notify(value);
77
- });
78
- NumberInput(this.currentDom, { ...this.options, onChange: handler });
59
+ NumberInput(this.currentDom, { ...this.options, onChange: value => {
60
+ var _a;
61
+ this.options.value = value;
62
+ this.writeModel(value);
63
+ this.onChange.notify(value);
64
+ (_a = this.renderer) === null || _a === void 0 ? void 0 : _a.refresh();
65
+ } });
66
+ }
67
+ onRemove() { this.currentDom = null; }
68
+ hasBinding() { return this.options.bindTo != null && this.options.property != null; }
69
+ readModel() {
70
+ var _a;
71
+ const value = this.options.bindTo[this.options.property];
72
+ return ((_a = this.options.converter) === null || _a === void 0 ? void 0 : _a.toUI) ? this.options.converter.toUI(value) : value;
73
+ }
74
+ writeModel(value) {
75
+ var _a;
76
+ if (this.hasBinding() && this.options.bindMode !== "fromModel") {
77
+ this.options.bindTo[this.options.property] = ((_a = this.options.converter) === null || _a === void 0 ? void 0 : _a.toData) ? this.options.converter.toData(value) : value;
78
+ this.modelValue = value;
79
+ }
79
80
  }
80
81
  }
@@ -1,5 +1,5 @@
1
1
  import "./overlay.css";
2
- import { IJSLComponent, IJSLVNode } from "jsl-render";
2
+ import { IJSLComponent, IJSLVNode, JSLRender } from "jsl-render";
3
3
  export interface IOverlayComponentOptions {
4
4
  content: IJSLVNode | string | IJSLComponent;
5
5
  showCloseButton?: boolean;
@@ -12,16 +12,22 @@ export interface IOverlayComponentOptions {
12
12
  size?: string;
13
13
  class?: string;
14
14
  style?: string;
15
+ ariaLabel?: string;
16
+ closeButtonLabel?: string;
15
17
  }
16
18
  export declare class OverlayComponent implements IJSLComponent {
17
- static show(options: IOverlayComponentOptions): OverlayComponent;
18
- private static lastRenderedDlg;
19
- private closeIcon;
20
- private onKeydownEvent;
21
- close(): void;
19
+ State: {
20
+ closed: boolean;
21
+ };
22
+ private dom;
23
+ private renderer;
24
+ private disposeHost?;
22
25
  private options;
26
+ static show(options: IOverlayComponentOptions): OverlayComponent;
23
27
  constructor(options: IOverlayComponentOptions);
28
+ onInit(renderer: JSLRender): void;
24
29
  onCreate(node: IJSLVNode): void;
30
+ onRemove(): void;
31
+ close(): void;
25
32
  render(): IJSLVNode;
26
- private toNode;
27
33
  }