x4js 2.3.10 → 2.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/aicli.md +194 -0
- package/aicontext.md +75 -3
- package/aiguide.md +67 -0
- package/cli/main.mjs +3 -1
- package/package.json +2 -1
- package/src/components/autocomplete/autocomplete.module.scss +28 -0
- package/src/components/autocomplete/autocomplete.ts +361 -0
- package/src/components/boxes/boxes.ts +66 -0
- package/src/components/button/button.ts +1 -1
- package/src/components/chart/chart.module.scss +126 -0
- package/src/components/chart/chart.ts +758 -0
- package/src/components/combobox/combobox.ts +5 -5
- package/src/components/components.ts +2 -0
- package/src/components/header/header.ts +38 -3
- package/src/components/listbox/listbox.module.scss +4 -5
- package/src/components/listbox/listbox.ts +1 -1
- package/src/components/select/select.module.scss +26 -17
- package/src/components/select/select.ts +60 -10
- package/src/components/spreadsheet/spreadsheet.module.scss +31 -0
- package/src/components/spreadsheet/spreadsheet.ts +546 -11
- package/src/components/textedit/textedit.ts +4 -4
- package/src/core/core_router.ts +15 -3
- package/src/core/core_svg.ts +2 -2
- package/readme.npm.md +0 -248
package/README.md
CHANGED
|
@@ -7,7 +7,19 @@ see [X4 home page](https://x4js.org)
|
|
|
7
7
|
## API Documentation
|
|
8
8
|
see [GitHib](https://rlibre.github.io/x4/index.html)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## Using x4js with an AI assistant
|
|
11
|
+
Three files written for AI assistants ship with the package (in `node_modules/x4js/`). Give them to your assistant before asking it to write x4js code:
|
|
12
|
+
|
|
13
|
+
- [aicontext.md](./aicontext.md): compact API reference, with exact signatures
|
|
14
|
+
- [aiguide.md](./aiguide.md): how to write idiomatic x4js code
|
|
15
|
+
- [aicli.md](./aicli.md): the `x4js` command line and `x4.config.json`
|
|
16
|
+
|
|
17
|
+
With Claude Code, install the x4js plugin instead: its skill reads these files from your project by itself.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
claude plugin marketplace add rlibre/x4
|
|
21
|
+
claude plugin install x4js@rlibre
|
|
22
|
+
```
|
|
11
23
|
|
|
12
24
|
---
|
|
13
25
|
|
package/aicli.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# x4js command line
|
|
2
|
+
|
|
3
|
+
`x4js` creates, serves and builds x4js applications. It bundles TypeScript and styles with esbuild; there is nothing else to configure.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
x4js create <project> [--template <name>] [--no-install]
|
|
7
|
+
x4js templates
|
|
8
|
+
x4js dev [--config <file>] [--host <host>] [--port <port>] [--http|--https] [--open]
|
|
9
|
+
x4js build [--config <file>] [--debug]
|
|
10
|
+
x4js --help | --version
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
The command ships with the `x4js` package. In a project created by `x4js create` it is already a dependency, and the npm scripts use it:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "x4js dev",
|
|
21
|
+
"build": "x4js build"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
npm run dev
|
|
28
|
+
npm run build
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Without a project yet, run it through npx: `npx x4js create my-app`.
|
|
32
|
+
|
|
33
|
+
### Which x4js runs
|
|
34
|
+
|
|
35
|
+
There can be two copies on a machine: the one in the project's `node_modules` and a global one (`npm install -g x4js`).
|
|
36
|
+
|
|
37
|
+
| You type | Copy used |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `npm run dev`, `npx x4js dev` | the project's own copy |
|
|
40
|
+
| `x4js dev` | the global copy |
|
|
41
|
+
|
|
42
|
+
A global copy older than the project's can behave differently. `dev` and `build` print their version on the first line; check it when something looks wrong.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
x4js 2.3.11
|
|
46
|
+
mode dev
|
|
47
|
+
config C:\work\my-app\x4.config.json
|
|
48
|
+
outdir C:\work\my-app\bin
|
|
49
|
+
server http://127.0.0.1:8000
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Commands
|
|
53
|
+
|
|
54
|
+
### `x4js create <project>`
|
|
55
|
+
|
|
56
|
+
Creates a new project in the folder `<project>` from a template.
|
|
57
|
+
|
|
58
|
+
| Option | Effect |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `--template <name>`, `-t <name>` | Template to use. Default: `app`. |
|
|
61
|
+
| `--no-install` | Do not run `npm install` after creating the files. |
|
|
62
|
+
|
|
63
|
+
- The project name becomes the package name: lowercase letters, digits, `.`, `_` and `-` only.
|
|
64
|
+
- The target folder must be empty or not exist.
|
|
65
|
+
- Templates are downloaded from the `rlibre/x4-templates` repository on GitHub, so the command needs network access.
|
|
66
|
+
- The `x4js` dependency of the new project is set to the version line of the command that created it (for example `2.3`).
|
|
67
|
+
|
|
68
|
+
### `x4js templates`
|
|
69
|
+
|
|
70
|
+
Lists the available templates with a one-line description of each.
|
|
71
|
+
|
|
72
|
+
### `x4js dev`
|
|
73
|
+
|
|
74
|
+
Builds the application, serves the output folder and rebuilds on every change.
|
|
75
|
+
|
|
76
|
+
| Option | Effect |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `--config <file>` | Config file to use. Default: `x4.config.json`. |
|
|
79
|
+
| `--host <host>` | Address to listen on. Default: `127.0.0.1`. |
|
|
80
|
+
| `--port <port>` | Port to listen on. Default: the first free port from 8000. |
|
|
81
|
+
| `--http`, `--https` | Force the protocol, whatever the config says. |
|
|
82
|
+
| `--open` | Open the browser once the server is ready. |
|
|
83
|
+
|
|
84
|
+
- The page reloads when a source or a copied file changes. A change that only touches CSS is applied without reloading.
|
|
85
|
+
- Build errors and warnings are printed in the terminal.
|
|
86
|
+
- Editing the config file restarts the build.
|
|
87
|
+
- HTTPS needs a certificate and a key, declared in `dev.tls` (see below).
|
|
88
|
+
|
|
89
|
+
### `x4js build`
|
|
90
|
+
|
|
91
|
+
Writes the application to the output folder.
|
|
92
|
+
|
|
93
|
+
| Option | Effect |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `--config <file>` | Config file to use. Default: `x4.config.json`. |
|
|
96
|
+
| `--debug` | Debug build: not minified, with source maps. |
|
|
97
|
+
|
|
98
|
+
The output folder is **emptied before each build**. Do not keep hand-written files in it: put them in the sources and list them in `copy`.
|
|
99
|
+
|
|
100
|
+
| | `dev` | `build --debug` | `build` |
|
|
101
|
+
|---|---|---|---|
|
|
102
|
+
| Minified | no | no | yes |
|
|
103
|
+
| Source maps | separate `.map` files | separate `.map` files | none |
|
|
104
|
+
| `DEBUG_MODE` | `true` | `true` | `false` |
|
|
105
|
+
| Live reload | yes | no | no |
|
|
106
|
+
|
|
107
|
+
## `x4.config.json`
|
|
108
|
+
|
|
109
|
+
The file sits at the root of the project. Every key is optional; without the file, the defaults below apply.
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"entryPoints": ["src/main.ts"],
|
|
114
|
+
"outdir": "./bin",
|
|
115
|
+
"copy": [
|
|
116
|
+
{ "from": "./src/assets", "to": "assets" },
|
|
117
|
+
{ "from": "./src/index.html", "to": "index.html" }
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| Key | Default | Meaning |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `entryPoints` | `["src/main.ts"]` | Files to bundle. `src/main.ts` produces `main.js` and `main.css`. |
|
|
125
|
+
| `outdir` | `"./bin"` | Output folder. It cannot be the project root. |
|
|
126
|
+
| `copy` | `[]` | Files or folders copied to the output after each successful build. `to` is relative to `outdir`. |
|
|
127
|
+
| `external` | `[]` | Module names left out of the bundle. |
|
|
128
|
+
| `define` | `{}` | Constants replaced at build time. Values are JavaScript expressions written as strings, for example `"\"prod\""`. |
|
|
129
|
+
| `dev` | | Dev server settings: `host`, `port`, `https`, `tls`. |
|
|
130
|
+
| `esbuild` | `{}` | Native esbuild options, applied last. They override everything above. |
|
|
131
|
+
|
|
132
|
+
Relative paths are resolved from the project root. Paths accept environment variables, written `$NAME` or `${NAME}`; an undefined variable is an error.
|
|
133
|
+
|
|
134
|
+
### The HTML page
|
|
135
|
+
|
|
136
|
+
The command does not generate an HTML page. Write `src/index.html` and copy it with `copy`, as in the example above. It loads the bundle by its output name:
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<!DOCTYPE html>
|
|
140
|
+
<html>
|
|
141
|
+
<head>
|
|
142
|
+
<meta charset="utf8">
|
|
143
|
+
<link rel="stylesheet" href="main.css">
|
|
144
|
+
<script src="main.js"></script>
|
|
145
|
+
</head>
|
|
146
|
+
</html>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### HTTPS in dev
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"dev": {
|
|
154
|
+
"https": true,
|
|
155
|
+
"tls": { "cert": "./certs/dev.crt", "key": "./certs/dev.key" }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## What the build handles
|
|
161
|
+
|
|
162
|
+
| Import | Result |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `.ts`, `.js` | Bundled into one script per entry point (browser, ES2020). |
|
|
165
|
+
| `.scss`, `.sass`, `.css` | Compiled and bundled into one stylesheet per entry point. |
|
|
166
|
+
| `.less` | Same, if the `less` package is installed in the project (`npm install -D less`). |
|
|
167
|
+
| `.svg` imported from a script | A `data:` URL string, usable as an icon. |
|
|
168
|
+
| `.png`, `.jpg`, `.jpeg`, `.ttf`, `.woff`, `.woff2` | Copied to `assets/` with a hashed name. |
|
|
169
|
+
|
|
170
|
+
Two constants are always defined:
|
|
171
|
+
|
|
172
|
+
| Constant | Value |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `DEBUG_MODE` | `true` in `dev` and `build --debug`, `false` in `build`. |
|
|
175
|
+
| `VERSION_ID` | Build date as a number, `YYMMDD`. |
|
|
176
|
+
|
|
177
|
+
Declare them once in your project to use them from TypeScript:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
declare const DEBUG_MODE: boolean;
|
|
181
|
+
declare const VERSION_ID: number;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Troubleshooting
|
|
185
|
+
|
|
186
|
+
| Symptom | Cause |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `index.html` is missing from the output folder | It is not listed in `copy`, or the command that ran is an older global copy. Compare the version on the first line with the project's. |
|
|
189
|
+
| A file placed in the output folder disappeared | `x4js build` empties the folder. Move the file to the sources and add it to `copy`. |
|
|
190
|
+
| `Cannot find package 'esbuild'` | The dependencies of the `x4js` copy being run are not installed. Run `npm install` in the project. |
|
|
191
|
+
| `Config file not found` | The file given to `--config` does not exist. Without `--config`, a missing `x4.config.json` is not an error. |
|
|
192
|
+
| `Environment variable '…' is not defined` | A path in the config uses `$NAME` and the variable is not set. |
|
|
193
|
+
| `LESS support requires the "less" package` | A `.less` file is imported and `less` is not installed in the project. |
|
|
194
|
+
| No colors in the terminal, or unwanted ones | Set `FORCE_COLOR=1` to force them, `NO_COLOR=1` to remove them. |
|
package/aicontext.md
CHANGED
|
@@ -17,6 +17,7 @@ import { ... } from 'x4js' // src/x4.ts
|
|
|
17
17
|
**Archived repository**: https://github.com/rlibre/x4js (read-only since March 2026)
|
|
18
18
|
**License**: MIT
|
|
19
19
|
**Quick start**: `npx x4js create demo` (list the templates with `npx x4js templates`)
|
|
20
|
+
**Claude Code plugin**: `claude plugin marketplace add rlibre/x4` then `claude plugin install x4js@rlibre` (skill that reads these files from the project)
|
|
20
21
|
|
|
21
22
|
---
|
|
22
23
|
|
|
@@ -26,7 +27,7 @@ import { ... } from 'x4js' // src/x4.ts
|
|
|
26
27
|
CoreElement<E> named timers + event surface (no DOM)
|
|
27
28
|
├── Component<P,E> base DOM component
|
|
28
29
|
│ ├── Box<P,E>
|
|
29
|
-
│ │ ├── HBox / VBox
|
|
30
|
+
│ │ ├── HBox / VBox → HSplitBox / VSplitBox
|
|
30
31
|
│ │ ├── StackBox → AssistBox
|
|
31
32
|
│ │ ├── GridBox / MasonryBox
|
|
32
33
|
│ │ ├── Form
|
|
@@ -35,10 +36,11 @@ CoreElement<E> named timers + event surface (no DOM)
|
|
|
35
36
|
│ │ ├── BtnGroup, Tabs, Video, Saturation
|
|
36
37
|
│ ├── Button, Input, Icon, Label, SimpleText, Checkbox, Radio, Link
|
|
37
38
|
│ ├── Listbox, Gridview, Treeview, Select, Combobox, Slider, Gauge
|
|
38
|
-
│ ├── Progress, TickLine, Canvas, Image,
|
|
39
|
+
│ ├── Progress, TickLine, Canvas, Image, Spreadsheet, MonacoEditor
|
|
40
|
+
│ ├── SvgComponent → Chart
|
|
39
41
|
│ ├── CSizer → HSizer / VSizer
|
|
40
42
|
│ ├── FileDialog, ScrollView, Viewport, Flex, Space
|
|
41
|
-
│ ├── HBox-based: ColorInput, Rating, Switch, Keyboard, TextEdit, Breadcrumbs
|
|
43
|
+
│ ├── HBox-based: ColorInput, Rating, Switch, Keyboard, TextEdit → AutoComplete, Breadcrumbs
|
|
42
44
|
│ └── VBox-based: Calendar, ColorPicker, FileDrop, Panel, PropertyGrid, TextArea
|
|
43
45
|
└── Application<E> singleton, no DOM of its own
|
|
44
46
|
|
|
@@ -608,6 +610,26 @@ class Combobox extends Component {
|
|
|
608
610
|
}
|
|
609
611
|
```
|
|
610
612
|
|
|
613
|
+
### `AutoComplete` — text field with on-demand suggestions
|
|
614
|
+
|
|
615
|
+
```ts
|
|
616
|
+
interface AutoCompleteProps extends TextEditProps { // label, labelWidth, required, placeholder, value...
|
|
617
|
+
search: (ev: EvSearch) => void // required; ev.text, ev.setItems(items) — may be called async
|
|
618
|
+
change? // EvAutoCompleteChange { value, item /* null = free text */ }
|
|
619
|
+
strict?: boolean // only a picked item (or empty) is accepted; default false
|
|
620
|
+
itemValue?: "text" | "id" // what goes into the field when an item is picked; default "text"
|
|
621
|
+
delay?: number // ms before search, default 250
|
|
622
|
+
minChars?: number // default 1
|
|
623
|
+
renderer?: (item: ListItem) => Component
|
|
624
|
+
}
|
|
625
|
+
class AutoComplete extends TextEdit {
|
|
626
|
+
getSelection(): ListboxID // id of the picked item, undefined for a free text
|
|
627
|
+
setSelection(item: ListItem): void; showDropDown(): void
|
|
628
|
+
}
|
|
629
|
+
// Use Combobox for a fixed, known list; AutoComplete when suggestions come from a search (server).
|
|
630
|
+
// In a Form the value is the field content (text or id, per itemValue).
|
|
631
|
+
```
|
|
632
|
+
|
|
611
633
|
### `ColorInput`
|
|
612
634
|
|
|
613
635
|
```ts
|
|
@@ -710,12 +732,32 @@ interface SpreadsheetProps extends ComponentProps {
|
|
|
710
732
|
columns: SpreadsheetColumn[] // required
|
|
711
733
|
footer?: boolean
|
|
712
734
|
rowClassifier?: RowClassifier
|
|
735
|
+
enterMove?: "down" | "right" | "none" // selection move after Enter in a cell editor (default "down")
|
|
713
736
|
click?, dblClick?, contextMenu?, selectionChange?
|
|
737
|
+
cellChange? // EvCellChange, fired before an edited value is written to the store
|
|
738
|
+
cellKey? // EvCellKey { key, row, col, uievent }, fired for each key before the default handling
|
|
739
|
+
}
|
|
740
|
+
interface SpreadsheetColumn { // GridColumn +
|
|
741
|
+
editable?: boolean | ((row, col) => boolean) // inline edition (off by default), col = column index
|
|
742
|
+
editor?: (row, col, value) => Component // custom editor, must implement queryInterface("form-element")
|
|
714
743
|
}
|
|
744
|
+
interface EvCellChange { row; col /* index */; colId /* store key */; value /* writable */; oldValue }
|
|
715
745
|
class Spreadsheet extends Component {
|
|
716
746
|
getSelection(): /* cell */; navigate(sens: kbNav): boolean; lock(lock): void
|
|
747
|
+
editCell(row, col, clear = false): boolean // cell must be visible
|
|
748
|
+
stopEdit(commit = true): void; isEditing(): boolean
|
|
717
749
|
}
|
|
718
750
|
|
|
751
|
+
// Inline edition: F2 / Enter / double-click / typing starts, Enter / Tab / click elsewhere validates, Escape cancels,
|
|
752
|
+
// Delete clears, editable checkboxes toggle on double-click or Space (a single click only selects).
|
|
753
|
+
// Default editor is an Input matching the column type.
|
|
754
|
+
// cellKey: key hook (function keys...), not fired while a cell is edited; ev.preventDefault() skips the default handling.
|
|
755
|
+
// cellChange: ev.preventDefault() refuses the value, ev.value can be replaced.
|
|
756
|
+
// Select / Combobox in a cell (the store holds the id, use `formatter` to display the text):
|
|
757
|
+
// { id: 2, title: "Status", width: 120, editable: true,
|
|
758
|
+
// formatter: ( id ) => states.find( x => x.id===id )?.text,
|
|
759
|
+
// editor: ( row, col, value ) => new Select( { items: states, value } ) }
|
|
760
|
+
|
|
719
761
|
// Store: in-memory cell store (extends CoreElement)
|
|
720
762
|
class Store extends CoreElement {
|
|
721
763
|
setData(row, col, data): void; getData(row, col): any; hasData(row, col?): boolean
|
|
@@ -724,6 +766,28 @@ class Store extends CoreElement {
|
|
|
724
766
|
}
|
|
725
767
|
```
|
|
726
768
|
|
|
769
|
+
### `Chart` — minimal svg chart (line, area, bar, pie, donut)
|
|
770
|
+
|
|
771
|
+
```ts
|
|
772
|
+
interface ChartProps extends SvgProps {
|
|
773
|
+
type?: "line" | "area" | "bar" | "pie" | "donut" // default "line"
|
|
774
|
+
series: ChartSerie[] // required; pie & donut use the first serie
|
|
775
|
+
labels?: string[] // default: 1, 2, 3...
|
|
776
|
+
xaxis?: ChartAxis; yaxis?: ChartAxis // { name?, ticks?, min?, max?, step? }, all computed by default
|
|
777
|
+
legend?: boolean // default: shown when 2+ named series
|
|
778
|
+
stacked?: boolean
|
|
779
|
+
formatter?: (value: number) => string // default: chartFormatUnit (1.5k, 2M...)
|
|
780
|
+
click? // EvChartClick { serie, index, value }
|
|
781
|
+
}
|
|
782
|
+
interface ChartSerie { name?; values: number[] /* undefined = hole */; color?; fill?; dots? }
|
|
783
|
+
class Chart extends SvgComponent {
|
|
784
|
+
setType(type): void; setSeries(series, labels?): void; setLabels(labels): void
|
|
785
|
+
setTicks(sens: "x" | "y", ticks: ChartTick[]): void
|
|
786
|
+
}
|
|
787
|
+
// Colors: CSS variables --chart-color-1..8 (or serie.color). Size it with width/height/flex like any component.
|
|
788
|
+
// Minimal by design: no dual axis, mixed types, time axis, zoom or animation — use a dedicated library for those.
|
|
789
|
+
```
|
|
790
|
+
|
|
727
791
|
### `PropertyGrid`
|
|
728
792
|
|
|
729
793
|
```ts
|
|
@@ -894,6 +958,13 @@ class Form extends Box {
|
|
|
894
958
|
## Sizers / viewport / misc
|
|
895
959
|
|
|
896
960
|
```ts
|
|
961
|
+
// PREFERRED for resizable panels: HSplitBox / VSplitBox (sizers are inserted automatically between panels).
|
|
962
|
+
// The panel with `flex` takes the remaining space, its neighbour is the one resized.
|
|
963
|
+
class HSplitBox extends HBox // panels side by side, resizable width: { content: Component[] }
|
|
964
|
+
class VSplitBox extends VBox // panels stacked, resizable height: { content: Component[] }
|
|
965
|
+
// new HSplitBox( { content: [ new Treeview( { width: 250 } ), new Panel( { flex: 1 } ) ] } )
|
|
966
|
+
|
|
967
|
+
// Low level: only place sizers by hand when a SplitBox does not fit (popup edges, header cells...).
|
|
897
968
|
class CSizer extends Component // base splitter
|
|
898
969
|
class HSizer extends CSizer // horizontal splitter
|
|
899
970
|
class VSizer extends CSizer // vertical splitter
|
|
@@ -1325,6 +1396,7 @@ When creating a new x4js project, use these files as a base.
|
|
|
1325
1396
|
|
|
1326
1397
|
`x4js dev` runs the dev server, `x4js build` writes `outdir` (add `--debug` for a debug build).
|
|
1327
1398
|
Both read `x4.config.json` (change the file with `--config <file>`).
|
|
1399
|
+
Full reference of the command line (commands, options, config keys, troubleshooting): `aicli.md`.
|
|
1328
1400
|
|
|
1329
1401
|
### `x4.config.json`
|
|
1330
1402
|
|
package/aiguide.md
CHANGED
|
@@ -132,6 +132,23 @@ super();
|
|
|
132
132
|
|
|
133
133
|
when the component expects props.
|
|
134
134
|
|
|
135
|
+
When writing a custom component, pass the whole props object: do not remove your own values from it before calling `super`. The base class keeps what it receives in `this.props`, so they stay readable later.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
// avoid: search and strict are lost for this.props
|
|
139
|
+
const { search, strict, ...base } = props;
|
|
140
|
+
super(base);
|
|
141
|
+
|
|
142
|
+
// prefer: override only what must not reach the parent
|
|
143
|
+
super({ ...props, type: "text" });
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Connect the event callbacks given in props with `mapPropEvents`, not with hand-written `if (props.x) this.on(...)`:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
this.mapPropEvents(props, "change", "search");
|
|
150
|
+
```
|
|
151
|
+
|
|
135
152
|
For derived application components, prefer building the component's main content explicitly after `super(props)`.
|
|
136
153
|
|
|
137
154
|
Ordinary nested x4 components may naturally use their `content` property where convenient.
|
|
@@ -192,6 +209,10 @@ Component objects
|
|
|
192
209
|
Real DOM
|
|
193
210
|
```
|
|
194
211
|
|
|
212
|
+
`addDOMEvent` belongs to custom components (a canvas-based widget, for example).
|
|
213
|
+
|
|
214
|
+
On standard components, use their props and events.
|
|
215
|
+
|
|
195
216
|
---
|
|
196
217
|
|
|
197
218
|
## 6. Objects vs JSX
|
|
@@ -327,6 +348,12 @@ export const deviceModel = new DeviceModel();
|
|
|
327
348
|
|
|
328
349
|
A model may encapsulate server access directly.
|
|
329
350
|
|
|
351
|
+
Views should never call `fetch`.
|
|
352
|
+
|
|
353
|
+
All server access goes through model classes deriving from one base class that owns the single `fetch` call (timeout, network failure, HTTP status, expired session, fetch mask).
|
|
354
|
+
|
|
355
|
+
Create one model per functional domain. It normally corresponds to one URL root.
|
|
356
|
+
|
|
330
357
|
Keep responsibilities simple:
|
|
331
358
|
|
|
332
359
|
```text id="t39sbp"
|
|
@@ -467,6 +494,18 @@ Do not introduce a JavaScript theme abstraction simply to mirror CSS variables.
|
|
|
467
494
|
|
|
468
495
|
Presentation belongs in CSS when CSS already solves the problem.
|
|
469
496
|
|
|
497
|
+
CSS styles a component; it never removes behaviour.
|
|
498
|
+
|
|
499
|
+
Do not hide a control a component creates: configure the component through its props (for example `closable: false`).
|
|
500
|
+
|
|
501
|
+
If the props cannot express it, say so instead of working around it.
|
|
502
|
+
|
|
503
|
+
To retheme an application, redefine the primary colour scale (`--color-primary-a5` … `--color-primary-a90`) once per theme.
|
|
504
|
+
|
|
505
|
+
Do not recolour components one by one, and do not override the variables derived from the scale.
|
|
506
|
+
|
|
507
|
+
A dark theme gets its own scale.
|
|
508
|
+
|
|
470
509
|
---
|
|
471
510
|
|
|
472
511
|
## 19. Browser and TypeScript capabilities
|
|
@@ -578,6 +617,34 @@ The goal is to build the application with the smallest set of mechanisms that ke
|
|
|
578
617
|
|
|
579
618
|
---
|
|
580
619
|
|
|
620
|
+
## 23. Translations
|
|
621
|
+
|
|
622
|
+
x4 starts in French.
|
|
623
|
+
|
|
624
|
+
Application texts are declared with `addTranslation` for each language.
|
|
625
|
+
|
|
626
|
+
Select the language before any component is built, since a component reads its texts at construction.
|
|
627
|
+
|
|
628
|
+
Call `selectLanguage` after `addTranslation`:
|
|
629
|
+
|
|
630
|
+
```ts
|
|
631
|
+
addTranslation("fr", { /* ... */ });
|
|
632
|
+
addTranslation("en", { /* ... */ });
|
|
633
|
+
|
|
634
|
+
selectLanguage("en");
|
|
635
|
+
|
|
636
|
+
// only now build the application components
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
Type each additional language after the first one, so the compiler reports a missing text:
|
|
640
|
+
|
|
641
|
+
```ts
|
|
642
|
+
const app_fr = { app: { open: "Ouvrir", close: "Fermer" } };
|
|
643
|
+
const app_en: typeof app_fr = { app: { open: "Open", close: "Close" } };
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
581
648
|
## Final rule
|
|
582
649
|
|
|
583
650
|
When generating x4 code:
|
package/cli/main.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { failure } from "./log.mjs";
|
|
4
|
+
import { failure, info } from "./log.mjs";
|
|
5
5
|
|
|
6
6
|
const cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
const packageFile = path.join(cliDir, "..", "package.json");
|
|
@@ -46,11 +46,13 @@ try {
|
|
|
46
46
|
break;
|
|
47
47
|
}
|
|
48
48
|
case "dev": {
|
|
49
|
+
info("x4js", version);
|
|
49
50
|
const { dev } = await import("./dev.mjs");
|
|
50
51
|
await dev(argv);
|
|
51
52
|
break;
|
|
52
53
|
}
|
|
53
54
|
case "build": {
|
|
55
|
+
info("x4js", version);
|
|
54
56
|
const { build } = await import("./build.mjs");
|
|
55
57
|
await build(argv);
|
|
56
58
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x4js",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/x4.ts",
|
|
6
6
|
"module": "src/x4.ts",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"src",
|
|
22
22
|
"bin",
|
|
23
23
|
"cli",
|
|
24
|
+
"aicli.md",
|
|
24
25
|
"aicontext.md",
|
|
25
26
|
"aiguide.md",
|
|
26
27
|
"README.md",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ___ ___ __
|
|
3
|
+
* \ \/ / / _
|
|
4
|
+
* \ / /_| |_
|
|
5
|
+
* / \____ _|
|
|
6
|
+
* /__/\__\ |_|
|
|
7
|
+
*
|
|
8
|
+
* @file autocomplete.module.scss
|
|
9
|
+
* @author Etienne Cochard
|
|
10
|
+
*
|
|
11
|
+
* @copyright (c) 2026 R-libre ingenierie
|
|
12
|
+
*
|
|
13
|
+
* Use of this source code is governed by an MIT-style license
|
|
14
|
+
* that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
15
|
+
**/
|
|
16
|
+
|
|
17
|
+
// the field itself is a textedit
|
|
18
|
+
|
|
19
|
+
.x4dropdownlist.autocomplete {
|
|
20
|
+
.x4item {
|
|
21
|
+
gap: 8px;
|
|
22
|
+
|
|
23
|
+
.id {
|
|
24
|
+
font-weight: 500;
|
|
25
|
+
min-width: 4em;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|