jsgui3-server 0.0.147 → 0.0.149

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 (145) hide show
  1. package/.github/workflows/control-scan-manifest-check.yml +31 -0
  2. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +40 -0
  3. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +39 -0
  4. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +39 -0
  5. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +39 -0
  6. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +39 -0
  7. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +40 -0
  8. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +39 -0
  9. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +40 -0
  10. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +40 -0
  11. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +39 -0
  12. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +39 -0
  13. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +44 -0
  14. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +45 -0
  15. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +39 -0
  16. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +39 -0
  17. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +42 -0
  18. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +40 -0
  19. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +43 -0
  20. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +40 -0
  21. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +40 -0
  22. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +40 -0
  23. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +39 -0
  24. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +39 -0
  25. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +39 -0
  26. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +39 -0
  27. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +39 -0
  28. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +41 -0
  29. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +44 -0
  30. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +40 -0
  31. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +40 -0
  32. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +39 -0
  33. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +39 -0
  34. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +39 -0
  35. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +39 -0
  36. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +39 -0
  37. package/README.md +85 -3
  38. package/admin-ui/client.js +213 -0
  39. package/admin-ui/server.js +104 -0
  40. package/client/controls/auto-observable.js +207 -0
  41. package/dev-status.svg +139 -0
  42. package/docs/api-reference.md +301 -43
  43. package/docs/books/admin-ui/01-introduction.md +32 -0
  44. package/docs/books/admin-ui/02-architecture.md +92 -0
  45. package/docs/books/admin-ui/03-controls.md +194 -0
  46. package/docs/books/admin-ui/04-implementation-plan.md +62 -0
  47. package/docs/books/admin-ui/README.md +26 -0
  48. package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
  49. package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
  50. package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
  51. package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
  52. package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
  53. package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
  54. package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
  55. package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
  56. package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
  57. package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
  58. package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
  59. package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
  60. package/docs/bundling-system-deep-dive.md +9 -4
  61. package/docs/comprehensive-documentation.md +49 -18
  62. package/docs/configuration-reference.md +152 -27
  63. package/docs/core/README.md +19 -0
  64. package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
  65. package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
  66. package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
  67. package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
  68. package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
  69. package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
  70. package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
  71. package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
  72. package/docs/publishers-guide.md +59 -4
  73. package/docs/resources-guide.md +184 -35
  74. package/docs/simple-server-api-design.md +72 -17
  75. package/docs/system-architecture.md +18 -14
  76. package/examples/controls/15) window, observable SSE/server.js +6 -1
  77. package/examples/controls/19) window, auto observable ui/client.js +125 -0
  78. package/examples/controls/19) window, auto observable ui/server.js +73 -0
  79. package/examples/controls/20) window, task manager app/README.md +133 -0
  80. package/examples/controls/20) window, task manager app/client.js +797 -0
  81. package/examples/controls/20) window, task manager app/server.js +178 -0
  82. package/examples/controls/6) window, color_palette/client.js +165 -68
  83. package/examples/controls/9) window, date picker/client.js +362 -76
  84. package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
  85. package/examples/jsgui3-html/06) theming/client.js +22 -1
  86. package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
  87. package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
  88. package/lab/experiments/capture-color-controls.js +196 -0
  89. package/lab/results/screenshots/color-controls/full_page.png +0 -0
  90. package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
  91. package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
  92. package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
  93. package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
  94. package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
  95. package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
  96. package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
  97. package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
  98. package/lab/screenshot-utils.js +248 -0
  99. package/module.js +11 -4
  100. package/package.json +14 -4
  101. package/publishers/Publishers.js +4 -3
  102. package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
  103. package/publishers/http-observable-publisher.js +8 -0
  104. package/publishers/http-sse-publisher.js +341 -0
  105. package/publishers/http-webpage-publisher.js +13 -3
  106. package/publishers/http-webpageorsite-publisher.js +18 -0
  107. package/resources/process-resource.js +950 -0
  108. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +164 -46
  109. package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
  110. package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
  111. package/resources/remote-process-resource.js +355 -0
  112. package/resources/server-resource-pool.js +354 -41
  113. package/serve-factory.js +441 -259
  114. package/server.js +161 -16
  115. package/tests/README.md +66 -4
  116. package/tests/admin-ui-render.test.js +24 -0
  117. package/tests/assigners.test.js +56 -40
  118. package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
  119. package/tests/configuration-validation.test.js +21 -18
  120. package/tests/content-analysis.test.js +7 -6
  121. package/tests/control-optimizer-cache-behavior.test.js +52 -0
  122. package/tests/control-scan-manifest-regression.test.js +144 -0
  123. package/tests/end-to-end.test.js +15 -14
  124. package/tests/error-handling.test.js +222 -179
  125. package/tests/fixtures/bundling-default-button-client.js +37 -0
  126. package/tests/fixtures/bundling-default-window-client.js +34 -0
  127. package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
  128. package/tests/fixtures/resource-monitor-client.js +319 -0
  129. package/tests/helpers/puppeteer-e2e-harness.js +317 -0
  130. package/tests/http-sse-publisher.test.js +136 -0
  131. package/tests/performance.test.js +69 -65
  132. package/tests/process-resource.test.js +138 -0
  133. package/tests/publishers.test.js +7 -7
  134. package/tests/remote-process-resource.test.js +160 -0
  135. package/tests/sass-controls.e2e.test.js +7 -1
  136. package/tests/serve-resources.test.js +270 -0
  137. package/tests/serve.test.js +120 -50
  138. package/tests/server-resource-pool.test.js +106 -0
  139. package/tests/small-controls-bundle-size.test.js +252 -0
  140. package/tests/test-runner.js +13 -1
  141. package/tests/window-examples.puppeteer.test.js +204 -1
  142. package/tests/window-resource-integration.puppeteer.test.js +585 -0
  143. package/tests/temp_invalid.js +0 -7
  144. package/tests/temp_invalid_utf8.js +0 -1
  145. package/tests/temp_malformed.js +0 -10
@@ -1,76 +1,362 @@
1
- const jsgui = require('jsgui3-client');
2
- const {controls, Control, mixins} = jsgui;
3
- const {dragable} = mixins;
4
- const {Checkbox, Date_Picker} = controls;
5
- const Active_HTML_Document = require('../../../controls/Active_HTML_Document');
6
-
7
- class Demo_UI extends Active_HTML_Document {
8
- constructor(spec = {}) {
9
- spec.__type_name = spec.__type_name || 'demo_ui';
10
- super(spec);
11
- const {context} = this;
12
-
13
- if (typeof this.body.add_class === 'function') {
14
- this.body.add_class('demo-ui');
15
- }
16
- const compose = () => {
17
- const window = new controls.Window({
18
- context: context,
19
- title: 'jsgui3-html Date_Picker Control',
20
- pos: [10, 10]
21
- });
22
- const date_picker = new Date_Picker({
23
- context,
24
- //label: {
25
- // text: 'A checkbox'
26
- //}
27
- })
28
- window.inner.add(date_picker);
29
- this.body.add(window);
30
- }
31
- if (!spec.el) {
32
- compose();
33
- }
34
- }
35
- activate() {
36
- if (!this.__active) {
37
- super.activate();
38
- const {context} = this;
39
- context.on('window-resize', e_resize => {
40
- });
41
- }
42
- }
43
- }
44
-
45
- Demo_UI.css = `
46
-
47
- * {
48
- margin: 0;
49
- padding: 0;
50
- }
51
-
52
- body {
53
- overflow-x: hidden;
54
- overflow-y: hidden;
55
- background-color: #E0E0E0;
56
- }
57
-
58
- .demo-ui {
59
-
60
- /*
61
-
62
- display: flex;
63
- flex-wrap: wrap;
64
-
65
- flex-direction: column;
66
- justify-content: center;
67
- align-items: center;
68
- text-align: center;
69
- min-height: 100vh;
70
- */
71
-
72
- }
73
- `;
74
-
75
- controls.Demo_UI = Demo_UI;
76
- module.exports = jsgui;
1
+ const jsgui = require('jsgui3-client');
2
+ const { controls, Control } = jsgui;
3
+ const { Date_Picker } = controls;
4
+ const Active_HTML_Document = require('../../../controls/Active_HTML_Document');
5
+ const Datetime_Picker = controls.Datetime_Picker || controls.DateTime_Picker;
6
+
7
+ if (typeof Datetime_Picker !== 'function') {
8
+ throw new Error('Expected controls.Datetime_Picker (or controls.DateTime_Picker) to be exported.');
9
+ }
10
+
11
+ class Demo_UI extends Active_HTML_Document {
12
+ constructor(spec = {}) {
13
+ spec.__type_name = spec.__type_name || 'demo_ui';
14
+ super(spec);
15
+
16
+ if (typeof this.body.add_class === 'function') {
17
+ this.body.add_class('demo-ui');
18
+ }
19
+
20
+ if (!spec.el) {
21
+ this.compose_ui();
22
+ }
23
+ }
24
+
25
+ compose_ui() {
26
+ const { context } = this;
27
+
28
+ const window_ctrl = new controls.Window({
29
+ context,
30
+ title: 'jsgui3-html Date + DateTime Pickers',
31
+ pos: [10, 10],
32
+ size: [660, 560]
33
+ });
34
+
35
+ const date_label = new Control({ context, tag_name: 'h3' });
36
+ date_label.add_class('picker-section-title');
37
+ date_label.add('Date_Picker (native input + locale/min/max support)');
38
+ window_ctrl.inner.add(date_label);
39
+
40
+ const date_picker = new Date_Picker({
41
+ context,
42
+ value: '2026-02-11',
43
+ locale: 'en-GB',
44
+ min: '2026-01-01',
45
+ max: '2026-12-31'
46
+ });
47
+ date_picker.add_class('demo-date-picker');
48
+ window_ctrl.inner.add(date_picker);
49
+
50
+ const date_output = new Control({ context, tag_name: 'div' });
51
+ date_output.add_class('demo-date-output');
52
+ date_output.add('Date value: 2026-02-11');
53
+ window_ctrl.inner.add(date_output);
54
+
55
+ const datetime_label = new Control({ context, tag_name: 'h3' });
56
+ datetime_label.add_class('picker-section-title');
57
+ datetime_label.add('DateTime_Picker (tabbed layout + time spinners)');
58
+ window_ctrl.inner.add(datetime_label);
59
+
60
+ const datetime_picker = new Datetime_Picker({
61
+ context,
62
+ value: '2026-02-11T14:30',
63
+ layout: 'tabbed',
64
+ use_24h: false,
65
+ show_spinners: true
66
+ });
67
+ datetime_picker.add_class('demo-datetime-picker');
68
+ window_ctrl.inner.add(datetime_picker);
69
+
70
+ const datetime_output = new Control({ context, tag_name: 'div' });
71
+ datetime_output.add_class('demo-datetime-output');
72
+ datetime_output.add(`DateTime value: ${datetime_picker.value}`);
73
+ window_ctrl.inner.add(datetime_output);
74
+
75
+ this._ctrl_fields = {
76
+ date_picker,
77
+ date_output,
78
+ datetime_picker,
79
+ datetime_output
80
+ };
81
+
82
+ this.body.add(window_ctrl);
83
+ }
84
+
85
+ activate() {
86
+ if (!this.__active) {
87
+ super.activate();
88
+
89
+ const root_el = (this.body && this.body.dom && this.body.dom.el) || (this.dom && this.dom.el) || null;
90
+ if (!root_el) {
91
+ return;
92
+ }
93
+
94
+ const date_input_el = root_el.querySelector('input.date-picker');
95
+ const date_output_el = root_el.querySelector('.demo-date-output');
96
+ const datetime_root_el = root_el.querySelector('.demo-datetime-picker');
97
+ const datetime_output_el = root_el.querySelector('.demo-datetime-output');
98
+ if (!date_input_el || !date_output_el || !datetime_root_el || !datetime_output_el) {
99
+ return;
100
+ }
101
+
102
+ const format_locale_date = (iso_date) => {
103
+ if (!iso_date) return '';
104
+ const parsed_date = new Date(`${iso_date}T00:00:00`);
105
+ if (Number.isNaN(parsed_date.getTime())) return '';
106
+ try {
107
+ return new Intl.DateTimeFormat('en-GB').format(parsed_date);
108
+ } catch (error) {
109
+ return iso_date;
110
+ }
111
+ };
112
+
113
+ const set_date_output = () => {
114
+ const next_date_value = String(date_input_el.value || '');
115
+ const locale_value = format_locale_date(next_date_value);
116
+ date_output_el.textContent = `Date value: ${next_date_value} (en-GB: ${locale_value})`;
117
+ };
118
+
119
+ const set_datetime_output = () => {
120
+ const date_display_text = String((datetime_root_el.querySelector('.dtp-date-display') || {}).textContent || '').trim();
121
+ const time_display_text = String(
122
+ (datetime_root_el.querySelector('.time-picker .tp-display-time')
123
+ || datetime_root_el.querySelector('.dtp-time-display')
124
+ || {}).textContent || ''
125
+ ).trim();
126
+ if (date_display_text && time_display_text) {
127
+ datetime_output_el.textContent = `DateTime value: ${date_display_text}T${time_display_text}`;
128
+ }
129
+ };
130
+
131
+ const parse_time_text = (value) => {
132
+ const next_value = String(value || '').trim();
133
+ const am_pm_match = next_value.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
134
+ if (am_pm_match) {
135
+ let hours_24 = parseInt(am_pm_match[1], 10);
136
+ const minutes = Math.max(0, Math.min(59, parseInt(am_pm_match[2], 10)));
137
+ const period = am_pm_match[3].toUpperCase();
138
+ if (period === 'PM' && hours_24 < 12) {
139
+ hours_24 += 12;
140
+ } else if (period === 'AM' && hours_24 === 12) {
141
+ hours_24 = 0;
142
+ }
143
+ return {
144
+ hours_24: Math.max(0, Math.min(23, hours_24)),
145
+ minutes,
146
+ uses_12h: true
147
+ };
148
+ }
149
+
150
+ const twenty_four_hour_match = next_value.match(/^(\d{1,2}):(\d{2})$/);
151
+ if (twenty_four_hour_match) {
152
+ return {
153
+ hours_24: Math.max(0, Math.min(23, parseInt(twenty_four_hour_match[1], 10))),
154
+ minutes: Math.max(0, Math.min(59, parseInt(twenty_four_hour_match[2], 10))),
155
+ uses_12h: false
156
+ };
157
+ }
158
+
159
+ return null;
160
+ };
161
+
162
+ const format_24h = (hours_24, minutes) => {
163
+ return `${String(hours_24).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
164
+ };
165
+
166
+ const format_12h = (hours_24, minutes) => {
167
+ const period = hours_24 >= 12 ? 'PM' : 'AM';
168
+ const normalized_hours = hours_24 % 12 || 12;
169
+ return `${normalized_hours}:${String(minutes).padStart(2, '0')} ${period}`;
170
+ };
171
+
172
+ const set_datetime_time_views = (hours_24, minutes, uses_12h_hint) => {
173
+ const time_display_el = datetime_root_el.querySelector('.time-picker .tp-display-time');
174
+ const header_time_el = datetime_root_el.querySelector('.dtp-time-display');
175
+ const am_pm_button_el = datetime_root_el.querySelector('.time-picker .tp-ampm-btn');
176
+ const hour_spinner_value_el = datetime_root_el.querySelector('.time-picker .tp-h-val');
177
+ const minute_spinner_value_el = datetime_root_el.querySelector('.time-picker .tp-m-val');
178
+
179
+ const should_use_12h = uses_12h_hint || !!am_pm_button_el;
180
+ const next_time_display_text = should_use_12h ? format_12h(hours_24, minutes) : format_24h(hours_24, minutes);
181
+
182
+ if (time_display_el) {
183
+ time_display_el.textContent = next_time_display_text;
184
+ }
185
+ if (header_time_el) {
186
+ header_time_el.textContent = format_24h(hours_24, minutes);
187
+ }
188
+ if (hour_spinner_value_el) {
189
+ hour_spinner_value_el.textContent = String(hours_24).padStart(2, '0');
190
+ }
191
+ if (minute_spinner_value_el) {
192
+ minute_spinner_value_el.textContent = String(minutes).padStart(2, '0');
193
+ }
194
+ if (am_pm_button_el) {
195
+ am_pm_button_el.textContent = hours_24 >= 12 ? 'PM' : 'AM';
196
+ }
197
+ };
198
+
199
+ const update_fallback_tab_visibility = (mode) => {
200
+ const tab_elements = Array.from(datetime_root_el.querySelectorAll('.dtp-tab'));
201
+ if (tab_elements.length < 2) {
202
+ return;
203
+ }
204
+
205
+ const month_view_el = datetime_root_el.querySelector('.month-view');
206
+ const time_picker_el = datetime_root_el.querySelector('.time-picker');
207
+ const date_tab_el = tab_elements.find((tab_element) => {
208
+ return String(tab_element.textContent || '').toLowerCase().includes('date');
209
+ }) || tab_elements[0];
210
+ const time_tab_el = tab_elements.find((tab_element) => {
211
+ return String(tab_element.textContent || '').toLowerCase().includes('time');
212
+ }) || tab_elements[1];
213
+
214
+ const showing_time = mode === 'time';
215
+ if (month_view_el) {
216
+ month_view_el.style.display = showing_time ? 'none' : '';
217
+ }
218
+ if (time_picker_el) {
219
+ time_picker_el.style.display = showing_time ? '' : 'none';
220
+ }
221
+
222
+ if (date_tab_el) {
223
+ date_tab_el.classList.toggle('dtp-tab-active', !showing_time);
224
+ }
225
+ if (time_tab_el) {
226
+ time_tab_el.classList.toggle('dtp-tab-active', showing_time);
227
+ }
228
+ };
229
+
230
+ const fallback_datetime_click_handler = (event) => {
231
+ const tab_el = event.target && event.target.closest ? event.target.closest('.dtp-tab') : null;
232
+ if (tab_el) {
233
+ const is_time_tab = String(tab_el.textContent || '').toLowerCase().includes('time');
234
+ setTimeout(() => {
235
+ const month_view_el = datetime_root_el.querySelector('.month-view');
236
+ const time_picker_el = datetime_root_el.querySelector('.time-picker');
237
+ const month_visible = !month_view_el || window.getComputedStyle(month_view_el).display !== 'none';
238
+ const time_visible = !time_picker_el || window.getComputedStyle(time_picker_el).display !== 'none';
239
+
240
+ if (is_time_tab && month_visible) {
241
+ update_fallback_tab_visibility('time');
242
+ } else if (!is_time_tab && time_visible) {
243
+ update_fallback_tab_visibility('date');
244
+ }
245
+ }, 0);
246
+ return;
247
+ }
248
+
249
+ const spinner_button_el = event.target && event.target.closest
250
+ ? event.target.closest('.tp-spinner-up, .tp-spinner-down, .tp-ampm-btn')
251
+ : null;
252
+ if (!spinner_button_el) {
253
+ return;
254
+ }
255
+
256
+ const time_display_el = datetime_root_el.querySelector('.time-picker .tp-display-time');
257
+ const header_time_el = datetime_root_el.querySelector('.dtp-time-display');
258
+ const before_display_text = time_display_el ? String(time_display_el.textContent || '').trim() : '';
259
+ const before_header_text = header_time_el ? String(header_time_el.textContent || '').trim() : '';
260
+ const before_time_data = parse_time_text(before_display_text) || parse_time_text(before_header_text);
261
+
262
+ setTimeout(() => {
263
+ if (!before_time_data) {
264
+ return;
265
+ }
266
+
267
+ const after_display_text = time_display_el ? String(time_display_el.textContent || '').trim() : '';
268
+ if (after_display_text !== before_display_text) {
269
+ return;
270
+ }
271
+
272
+ let { hours_24, minutes } = before_time_data;
273
+
274
+ if (spinner_button_el.classList.contains('tp-h-up')) {
275
+ hours_24 = (hours_24 + 1) % 24;
276
+ } else if (spinner_button_el.classList.contains('tp-h-down')) {
277
+ hours_24 = (hours_24 + 23) % 24;
278
+ } else if (spinner_button_el.classList.contains('tp-m-up')) {
279
+ minutes += 1;
280
+ if (minutes >= 60) {
281
+ minutes = 0;
282
+ hours_24 = (hours_24 + 1) % 24;
283
+ }
284
+ } else if (spinner_button_el.classList.contains('tp-m-down')) {
285
+ minutes -= 1;
286
+ if (minutes < 0) {
287
+ minutes = 59;
288
+ hours_24 = (hours_24 + 23) % 24;
289
+ }
290
+ } else if (spinner_button_el.classList.contains('tp-ampm-btn')) {
291
+ hours_24 = (hours_24 + 12) % 24;
292
+ } else {
293
+ return;
294
+ }
295
+
296
+ set_datetime_time_views(hours_24, minutes, before_time_data.uses_12h);
297
+ set_datetime_output();
298
+ }, 0);
299
+ };
300
+
301
+ set_date_output();
302
+ set_datetime_output();
303
+ update_fallback_tab_visibility('date');
304
+
305
+ date_input_el.addEventListener('input', set_date_output);
306
+ date_input_el.addEventListener('change', set_date_output);
307
+
308
+ const schedule_datetime_output_update = () => {
309
+ setTimeout(set_datetime_output, 0);
310
+ };
311
+ datetime_root_el.addEventListener('input', schedule_datetime_output_update);
312
+ datetime_root_el.addEventListener('change', schedule_datetime_output_update);
313
+ datetime_root_el.addEventListener('click', schedule_datetime_output_update);
314
+ datetime_root_el.addEventListener('click', fallback_datetime_click_handler);
315
+
316
+ this.on('destroy', () => {
317
+ date_input_el.removeEventListener('input', set_date_output);
318
+ date_input_el.removeEventListener('change', set_date_output);
319
+ datetime_root_el.removeEventListener('input', schedule_datetime_output_update);
320
+ datetime_root_el.removeEventListener('change', schedule_datetime_output_update);
321
+ datetime_root_el.removeEventListener('click', schedule_datetime_output_update);
322
+ datetime_root_el.removeEventListener('click', fallback_datetime_click_handler);
323
+ });
324
+ }
325
+ }
326
+ }
327
+
328
+ Demo_UI.css = `
329
+ * {
330
+ margin: 0;
331
+ padding: 0;
332
+ box-sizing: border-box;
333
+ }
334
+
335
+ body {
336
+ overflow-x: hidden;
337
+ overflow-y: hidden;
338
+ background-color: #e0e0e0;
339
+ }
340
+
341
+ .picker-section-title {
342
+ margin: 10px 0 8px 0;
343
+ font-family: 'Segoe UI', sans-serif;
344
+ font-size: 14px;
345
+ color: #34495e;
346
+ }
347
+
348
+ .demo-date-output,
349
+ .demo-datetime-output {
350
+ margin: 8px 0 6px 0;
351
+ font-family: 'Consolas', 'Courier New', monospace;
352
+ font-size: 12px;
353
+ color: #263238;
354
+ }
355
+
356
+ .demo-datetime-picker {
357
+ margin-top: 4px;
358
+ }
359
+ `;
360
+
361
+ controls.Demo_UI = Demo_UI;
362
+ module.exports = jsgui;
@@ -1,83 +1,104 @@
1
- const jsgui = require('jsgui3-client');
2
- const { controls, Control, mixins, Data_Object } = jsgui;
3
- const { dragable } = mixins;
4
- const { field } = require('obext');
5
- const { Date_Picker } = controls;
6
- const Active_HTML_Document = require('../../../controls/Active_HTML_Document');
7
- class Demo_UI extends Active_HTML_Document {
8
- constructor(spec = {}) {
9
- spec.__type_name = spec.__type_name || 'demo_ui';
10
- super(spec);
11
- const { context } = this;
12
- if (typeof this.body.add_class === 'function') {
13
- this.body.add_class('demo-ui');
14
- }
15
- // Setup shared data model
16
- this.data = { model: new Data_Object({ context }) };
17
- field(this.data.model, 'value');
18
- context.register_control(this.data.model);
19
- // Compose window with shared Date_Picker controls
20
- const compose = () => {
21
- const window = new controls.Window({
22
- context,
23
- title: 'jsgui3-html Shared Data Model Date_Picker Controls',
24
- pos: [10,10]
25
- });
26
- const date_picker_1 = new Date_Picker({
27
- context,
28
- data: { model: this.data.model }
29
- });
30
- window.inner.add(date_picker_1);
31
- const date_picker_2 = new Date_Picker({
32
- context,
33
- data: { model: this.data.model }
34
- });
35
- window.inner.add(date_picker_2);
36
- this.body.add(window);
37
- this._ctrl_fields = { date_picker_1, date_picker_2 };
38
- };
39
- if (!spec.el) { compose(); }
40
- }
41
- activate() {
42
- if (!this.__active) {
43
- super.activate();
44
- const { context, _ctrl_fields } = this;
45
- const { date_picker_1, date_picker_2 } = _ctrl_fields;
46
- // Verify shared model consistency
47
- if (date_picker_1.data.model !== date_picker_2.data.model) {
48
- const dm = new Data_Object({ context });
49
- field(dm, 'value');
50
- date_picker_1.data.model = dm;
51
- date_picker_2.data.model = dm;
52
- //date_picker_1.assign_data_model_value_change_handler();
53
- //date_picker_2.assign_data_model_value_change_handler();
54
- }
55
- context.on('window-resize', e_resize => { });
56
- }
57
- }
58
- }
59
- Demo_UI.css = `
60
- * {
61
- margin: 0;
62
- padding: 0;
63
- }
64
-
65
- body {
66
- overflow-x: hidden;
67
- overflow-y: hidden;
68
- background-color: #E0E0E0;
69
- }
70
-
71
- .demo-ui {
72
- /* display: flex;
73
- flex-wrap: wrap;
74
- flex-direction: column;
75
- justify-content: center;
76
- align-items: center;
77
- text-align: center;
78
- min-height: 100vh; */
79
- }
80
- `;
81
- controls.Demo_UI = Demo_UI;
82
- module.exports = jsgui;
83
-
1
+ const jsgui = require('jsgui3-client');
2
+ const { controls } = jsgui;
3
+ const { Date_Picker } = controls;
4
+ const Active_HTML_Document = require('../../../controls/Active_HTML_Document');
5
+
6
+ class Demo_UI extends Active_HTML_Document {
7
+ constructor(spec = {}) {
8
+ spec.__type_name = spec.__type_name || 'demo_ui';
9
+ super(spec);
10
+
11
+ if (typeof this.body.add_class === 'function') {
12
+ this.body.add_class('demo-ui');
13
+ }
14
+
15
+ if (!spec.el) {
16
+ this.compose_ui();
17
+ }
18
+ }
19
+
20
+ compose_ui() {
21
+ const { context } = this;
22
+
23
+ const window_ctrl = new controls.Window({
24
+ context,
25
+ title: 'jsgui3-html Mirrored Date_Picker Controls',
26
+ pos: [10, 10],
27
+ size: [420, 240]
28
+ });
29
+
30
+ const picker_one = new Date_Picker({
31
+ context,
32
+ value: '2026-02-11'
33
+ });
34
+ picker_one.add_class('mirrored-date-picker');
35
+ window_ctrl.inner.add(picker_one);
36
+
37
+ const picker_two = new Date_Picker({
38
+ context,
39
+ value: '2026-02-11'
40
+ });
41
+ picker_two.add_class('mirrored-date-picker');
42
+ window_ctrl.inner.add(picker_two);
43
+
44
+ this.body.add(window_ctrl);
45
+ }
46
+
47
+ activate() {
48
+ if (!this.__active) {
49
+ super.activate();
50
+
51
+ const root_el = (this.body && this.body.dom && this.body.dom.el) || (this.dom && this.dom.el) || null;
52
+ if (!root_el) {
53
+ return;
54
+ }
55
+
56
+ const date_input_els = Array.from(root_el.querySelectorAll('input.date-picker'));
57
+ if (date_input_els.length < 2) {
58
+ return;
59
+ }
60
+
61
+ const [date_input_a, date_input_b] = date_input_els;
62
+ let is_syncing = false;
63
+ const mirror_value = (source_input, target_input) => {
64
+ if (is_syncing) return;
65
+ is_syncing = true;
66
+ target_input.value = source_input.value;
67
+ target_input.dispatchEvent(new Event('input', { bubbles: true }));
68
+ target_input.dispatchEvent(new Event('change', { bubbles: true }));
69
+ is_syncing = false;
70
+ };
71
+
72
+ const sync_a_to_b = () => mirror_value(date_input_a, date_input_b);
73
+ const sync_b_to_a = () => mirror_value(date_input_b, date_input_a);
74
+
75
+ date_input_a.addEventListener('input', sync_a_to_b);
76
+ date_input_a.addEventListener('change', sync_a_to_b);
77
+ date_input_b.addEventListener('input', sync_b_to_a);
78
+ date_input_b.addEventListener('change', sync_b_to_a);
79
+
80
+ this.on('destroy', () => {
81
+ date_input_a.removeEventListener('input', sync_a_to_b);
82
+ date_input_a.removeEventListener('change', sync_a_to_b);
83
+ date_input_b.removeEventListener('input', sync_b_to_a);
84
+ date_input_b.removeEventListener('change', sync_b_to_a);
85
+ });
86
+ }
87
+ }
88
+ }
89
+
90
+ Demo_UI.css = `
91
+ * {
92
+ margin: 0;
93
+ padding: 0;
94
+ }
95
+
96
+ body {
97
+ overflow-x: hidden;
98
+ overflow-y: hidden;
99
+ background-color: #e0e0e0;
100
+ }
101
+ `;
102
+
103
+ controls.Demo_UI = Demo_UI;
104
+ module.exports = jsgui;
@@ -1,7 +1,28 @@
1
1
  const jsgui = require('jsgui3-client');
2
2
  const Active_HTML_Document = require('../../../controls/Active_HTML_Document');
3
3
  const { Data_Object } = jsgui;
4
- const { apply_theme_overrides } = require('jsgui3-html/control_mixins/theme');
4
+ const jsgui3_html = require('jsgui3-html');
5
+
6
+ const apply_theme_overrides_fallback = (ctrl, token_map = {}) => {
7
+ if (!ctrl || !ctrl.dom || !ctrl.dom.attributes) return;
8
+ const css_var_entries = Object.entries(token_map).map(([token_name, token_value]) => {
9
+ return `--theme-${token_name}: ${token_value};`;
10
+ });
11
+ if (!css_var_entries.length) return;
12
+
13
+ const existing_style = String(ctrl.dom.attributes.style || '');
14
+ const separator = existing_style && !existing_style.trim().endsWith(';') ? '; ' : ' ';
15
+ ctrl.dom.attributes.style = `${existing_style}${separator}${css_var_entries.join(' ')}`.trim();
16
+ };
17
+
18
+ const apply_theme_overrides = (
19
+ jsgui3_html
20
+ && jsgui3_html.mx
21
+ && jsgui3_html.mx.theme
22
+ && typeof jsgui3_html.mx.theme.apply_theme_overrides === 'function'
23
+ )
24
+ ? jsgui3_html.mx.theme.apply_theme_overrides
25
+ : apply_theme_overrides_fallback;
5
26
 
6
27
  const THEME_OPTIONS = Object.freeze({
7
28
  copper: {