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.
- package/.github/workflows/control-scan-manifest-check.yml +31 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +44 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +45 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +42 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +43 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +41 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +44 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +39 -0
- package/README.md +85 -3
- package/admin-ui/client.js +213 -0
- package/admin-ui/server.js +104 -0
- package/client/controls/auto-observable.js +207 -0
- package/dev-status.svg +139 -0
- package/docs/api-reference.md +301 -43
- package/docs/books/admin-ui/01-introduction.md +32 -0
- package/docs/books/admin-ui/02-architecture.md +92 -0
- package/docs/books/admin-ui/03-controls.md +194 -0
- package/docs/books/admin-ui/04-implementation-plan.md +62 -0
- package/docs/books/admin-ui/README.md +26 -0
- package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
- package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
- package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
- package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
- package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
- package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
- package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
- package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
- package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
- package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
- package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
- package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
- package/docs/bundling-system-deep-dive.md +9 -4
- package/docs/comprehensive-documentation.md +49 -18
- package/docs/configuration-reference.md +152 -27
- package/docs/core/README.md +19 -0
- package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
- package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
- package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
- package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
- package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
- package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
- package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
- package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
- package/docs/publishers-guide.md +59 -4
- package/docs/resources-guide.md +184 -35
- package/docs/simple-server-api-design.md +72 -17
- package/docs/system-architecture.md +18 -14
- package/examples/controls/15) window, observable SSE/server.js +6 -1
- package/examples/controls/19) window, auto observable ui/client.js +125 -0
- package/examples/controls/19) window, auto observable ui/server.js +73 -0
- package/examples/controls/20) window, task manager app/README.md +133 -0
- package/examples/controls/20) window, task manager app/client.js +797 -0
- package/examples/controls/20) window, task manager app/server.js +178 -0
- package/examples/controls/6) window, color_palette/client.js +165 -68
- package/examples/controls/9) window, date picker/client.js +362 -76
- package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
- package/examples/jsgui3-html/06) theming/client.js +22 -1
- package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
- package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
- package/lab/experiments/capture-color-controls.js +196 -0
- package/lab/results/screenshots/color-controls/full_page.png +0 -0
- package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
- package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
- package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
- package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
- package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
- package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
- package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
- package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
- package/lab/screenshot-utils.js +248 -0
- package/module.js +11 -4
- package/package.json +14 -4
- package/publishers/Publishers.js +4 -3
- package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
- package/publishers/http-observable-publisher.js +8 -0
- package/publishers/http-sse-publisher.js +341 -0
- package/publishers/http-webpage-publisher.js +13 -3
- package/publishers/http-webpageorsite-publisher.js +18 -0
- package/resources/process-resource.js +950 -0
- package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +164 -46
- package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
- package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
- package/resources/remote-process-resource.js +355 -0
- package/resources/server-resource-pool.js +354 -41
- package/serve-factory.js +441 -259
- package/server.js +161 -16
- package/tests/README.md +66 -4
- package/tests/admin-ui-render.test.js +24 -0
- package/tests/assigners.test.js +56 -40
- package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
- package/tests/configuration-validation.test.js +21 -18
- package/tests/content-analysis.test.js +7 -6
- package/tests/control-optimizer-cache-behavior.test.js +52 -0
- package/tests/control-scan-manifest-regression.test.js +144 -0
- package/tests/end-to-end.test.js +15 -14
- package/tests/error-handling.test.js +222 -179
- package/tests/fixtures/bundling-default-button-client.js +37 -0
- package/tests/fixtures/bundling-default-window-client.js +34 -0
- package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
- package/tests/fixtures/resource-monitor-client.js +319 -0
- package/tests/helpers/puppeteer-e2e-harness.js +317 -0
- package/tests/http-sse-publisher.test.js +136 -0
- package/tests/performance.test.js +69 -65
- package/tests/process-resource.test.js +138 -0
- package/tests/publishers.test.js +7 -7
- package/tests/remote-process-resource.test.js +160 -0
- package/tests/sass-controls.e2e.test.js +7 -1
- package/tests/serve-resources.test.js +270 -0
- package/tests/serve.test.js +120 -50
- package/tests/server-resource-pool.test.js +106 -0
- package/tests/small-controls-bundle-size.test.js +252 -0
- package/tests/test-runner.js +13 -1
- package/tests/window-examples.puppeteer.test.js +204 -1
- package/tests/window-resource-integration.puppeteer.test.js +585 -0
- package/tests/temp_invalid.js +0 -7
- package/tests/temp_invalid_utf8.js +0 -1
- package/tests/temp_malformed.js +0 -10
|
@@ -1,76 +1,362 @@
|
|
|
1
|
-
const jsgui = require('jsgui3-client');
|
|
2
|
-
const {controls, Control
|
|
3
|
-
const {
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
3
|
-
const {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
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: {
|