x4js 2.3.27 → 2.3.29
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/LICENSE +21 -0
- package/README.md +5 -0
- package/aicontext.md +412 -24
- package/package.json +8 -3
- package/src/components/autocomplete/autocomplete.ts +5 -1
- package/src/components/bargraph/bargraph.module.scss +44 -0
- package/src/components/bargraph/bargraph.ts +362 -0
- package/src/components/base.scss +71 -1
- package/src/components/boxes/boxes.ts +2 -2
- package/src/components/button/button.module.scss +4 -3
- package/src/components/calendar/calendar.module.scss +6 -5
- package/src/components/calendar/calendar.ts +14 -8
- package/src/components/canvas/canvas.ts +5 -4
- package/src/components/carousel/carousel.module.scss +165 -0
- package/src/components/carousel/carousel.ts +423 -0
- package/src/components/chart/chart.module.scss +8 -0
- package/src/components/chart/chart.ts +170 -12
- package/src/components/checkbox/checkbox.module.scss +67 -1
- package/src/components/checkbox/checkbox.ts +22 -6
- package/src/components/colorinput/colorinput.module.scss +1 -0
- package/src/components/combobox/combobox.module.scss +6 -2
- package/src/components/components.ts +3 -0
- package/src/components/gantt/gantt.module.scss +462 -0
- package/src/components/gantt/gantt.ts +1254 -0
- package/src/components/gridview/gridview.module.scss +6 -0
- package/src/components/input/input.module.scss +1 -1
- package/src/components/knob/knob.module.scss +113 -0
- package/src/components/knob/knob.ts +355 -0
- package/src/components/mapview/mapview.module.scss +177 -0
- package/src/components/mapview/mapview.ts +764 -0
- package/src/components/messages/messages.module.scss +9 -4
- package/src/components/planner/planner.module.scss +688 -0
- package/src/components/planner/planner.ts +1792 -0
- package/src/components/propgrid/progrid.module.scss +13 -5
- package/src/components/radio/radio.module.scss +58 -32
- package/src/components/radio/radio.ts +12 -1
- package/src/components/sizers/sizer.ts +17 -5
- package/src/components/slider/slider.module.scss +22 -20
- package/src/components/spreadsheet/spreadsheet.module.scss +6 -0
- package/src/components/switch/switch.module.scss +68 -44
- package/src/components/switch/switch.ts +6 -3
- package/src/components/tabs/tabs.module.scss +2 -0
- package/src/components/tabs/tabs.ts +1 -0
- package/src/components/tag/tag.module.scss +4 -4
- package/src/components/textarea/textarea.module.scss +9 -0
- package/src/components/textarea/textarea.ts +1 -0
- package/src/components/textedit/textedit.module.scss +5 -2
- package/src/components/themes.scss +20 -6
- package/src/components/treeview/treeview.module.scss +6 -1
- package/src/components/treeview/treeview.ts +1 -0
- package/src/components/video/video.module.scss +5 -2
- package/src/components/video/video.ts +14 -1
- package/src/core/core_i18n.ts +65 -0
- package/src/core/core_pdf.ts +17 -0
- package/src/core/core_react.ts +12 -18
- package/src/core/core_svg.ts +9 -6
- package/src/core/core_tools.ts +44 -0
- package/src/x4doc.ts +3 -0
package/aicontext.md
CHANGED
|
@@ -35,11 +35,13 @@ CoreElement<E> named timers + event surface (no DOM)
|
|
|
35
35
|
│ │ │ → Menu, DropdownList, Notification
|
|
36
36
|
│ │ ├── BtnGroup, Tabs, Video, Saturation
|
|
37
37
|
│ ├── Button, Input, Icon, Label, SimpleText, Checkbox, Radio, Link, SimpleTextArea
|
|
38
|
-
│ ├── Listbox, Gridview, Treeview, Select, Combobox, Slider, Gauge
|
|
38
|
+
│ ├── Listbox, Gridview, Treeview, Select, Combobox, Slider, Knob, Gauge
|
|
39
39
|
│ ├── Progress, TickLine, Timeline, Canvas, Image, Spreadsheet, MonacoEditor
|
|
40
|
+
│ ├── Gantt, Planner, MapView (each one from its entry: x4js/gantt, x4js/planner, x4js/mapview)
|
|
41
|
+
│ ├── Canvas → Bargraph
|
|
40
42
|
│ ├── SvgComponent → Chart
|
|
41
43
|
│ ├── CSizer → HSizer / VSizer
|
|
42
|
-
│ ├── FileDialog, ScrollView, Viewport, DockingView, Flex, Space
|
|
44
|
+
│ ├── FileDialog, ScrollView, Viewport, DockingView, Carousel, Flex, Space
|
|
43
45
|
│ ├── HBox-based: ColorInput, Rating, Switch, Tag, Keyboard, TextEdit → AutoComplete, Breadcrumbs
|
|
44
46
|
│ └── VBox-based: Calendar, ColorPicker, FileDrop, Panel, PropertyGrid, TextArea, SettingsView, SettingItem
|
|
45
47
|
└── Application<E> singleton, no DOM of its own
|
|
@@ -382,7 +384,7 @@ class StackBox extends Box {
|
|
|
382
384
|
}
|
|
383
385
|
```
|
|
384
386
|
|
|
385
|
-
### `AssistBox` — sequential navigation (wizard
|
|
387
|
+
### `AssistBox` — sequential navigation (wizard)
|
|
386
388
|
|
|
387
389
|
```ts
|
|
388
390
|
class AssistBox extends StackBox {
|
|
@@ -390,6 +392,52 @@ class AssistBox extends StackBox {
|
|
|
390
392
|
isFirstPage(): boolean
|
|
391
393
|
isLastPage(): boolean
|
|
392
394
|
}
|
|
395
|
+
// one page shown, no animation nor commands: for slides that move (arrows, dots, swipe, autoplay), Carousel
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### `Carousel` — slides that move: arrows, dots, swipe, autoplay
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
interface CarouselImage { // a slide that is an image: loaded when it comes near the view
|
|
402
|
+
src: string; alt?: string
|
|
403
|
+
caption?: string | UnsafeHtml // over the bottom of the image
|
|
404
|
+
fit?: "cover" | "contain" // cover by default
|
|
405
|
+
}
|
|
406
|
+
type CarouselItem = Component | CarouselImage
|
|
407
|
+
|
|
408
|
+
interface CarouselProps extends Omit<ComponentProps, "content"> {
|
|
409
|
+
items?: CarouselItem[]
|
|
410
|
+
perView?: number // slides seen side by side, 1 by default; a move goes one slide further
|
|
411
|
+
index?: number // the first slide seen at the start, 0 by default
|
|
412
|
+
loop?: boolean // after the last, the first (and before the first, the last)
|
|
413
|
+
autoplay?: number // ms between two slides; it loops; paused under the pointer, with the focus in it,
|
|
414
|
+
// during a swipe, or while the page is hidden
|
|
415
|
+
arrows?: boolean; dots?: boolean // true by default; none of them when all the slides are seen
|
|
416
|
+
change?: EventCallback<EvChange> // value: the index; the user (arrows, dots, swipe, keys) or the autoplay, not the code
|
|
417
|
+
}
|
|
418
|
+
class Carousel extends Component {
|
|
419
|
+
setItems(items: CarouselItem[], index?: number): void
|
|
420
|
+
select(index: number): void // no change event; beyond the ends: the end
|
|
421
|
+
next(): void; prev(): void // no change event; they loop when loop (or autoplay)
|
|
422
|
+
getIndex(): number; getCount(): number
|
|
423
|
+
}
|
|
424
|
+
// Its height is given (height, flex): the slides fill it. Without loop, an arrow is hidden at its end.
|
|
425
|
+
// Swipe: the finger or the mouse; a fifth of a slide goes to the next, a long swipe further; across, the page scrolls;
|
|
426
|
+
// a swipe is not a click on what is in the slide. Keys: ← → Home End.
|
|
427
|
+
// The slides out of the view are inert (Tab does not go in them). Disabled: faded as a Box, its images grey.
|
|
428
|
+
// The texts for screen readers: _tr.global.carousel. The autoplay timer stops when it leaves the page.
|
|
429
|
+
// CSS vars: --carousel-gap --carousel-duration --carousel-arrow-background --carousel-arrow-color --carousel-dot
|
|
430
|
+
// --carousel-dot-active --carousel-radius --carousel-caption-color --carousel-caption-background
|
|
431
|
+
// --carousel-disabled-filter
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
new Carousel({
|
|
436
|
+
height: 300, autoplay: 5000,
|
|
437
|
+
items: photos.map(p => ({ src: p.url, alt: p.title, caption: p.title })),
|
|
438
|
+
change: ev => this.showInfo(photos[ev.value]),
|
|
439
|
+
})
|
|
440
|
+
new Carousel({ height: 180, perView: 3, items: products.map(p => new ProductCard({ product: p })) })
|
|
393
441
|
```
|
|
394
442
|
|
|
395
443
|
### `GridBox`
|
|
@@ -578,7 +626,8 @@ interface TagProps extends ComponentProps {
|
|
|
578
626
|
}
|
|
579
627
|
class Tag extends HBox {} // no method: its text and its icon are those of its props
|
|
580
628
|
// cls "alert": the colors of an alert
|
|
581
|
-
// CSS vars: --tag-background --tag-color --tag-icon-color
|
|
629
|
+
// CSS vars: --tag-background --tag-color (the text on the accent) --tag-border; --tag-icon-color is not set:
|
|
630
|
+
// the icon has the color of the text, a class that changes --tag-color changes both
|
|
582
631
|
```
|
|
583
632
|
|
|
584
633
|
```ts
|
|
@@ -605,6 +654,41 @@ class Gauge extends Component {
|
|
|
605
654
|
}
|
|
606
655
|
```
|
|
607
656
|
|
|
657
|
+
### `Bargraph` — a level meter of segments, drawn in a canvas
|
|
658
|
+
|
|
659
|
+
```ts
|
|
660
|
+
interface BargraphProps extends ComponentProps {
|
|
661
|
+
value?: number // min by default
|
|
662
|
+
min?: number; max?: number // 0 and 100 by default
|
|
663
|
+
vertical?: boolean // the segments go up from the bottom; else from the left
|
|
664
|
+
segments?: number // 20 by default
|
|
665
|
+
colors?: string[] // from min to max, as Gauge (linearColorStops: green to red); one: a plain bar;
|
|
666
|
+
// none: the accent, pale to full (--bargraph-color-start, -end)
|
|
667
|
+
overshoot?: number // the segments from this value have the alert color; the colors go under it
|
|
668
|
+
animated?: boolean // up quickly, down slowly, in a few frames; false: at once (true by default)
|
|
669
|
+
peak?: boolean // the highest segment reached stays lit a second, then falls
|
|
670
|
+
}
|
|
671
|
+
class Bargraph extends Canvas {
|
|
672
|
+
setValue(v: number): void // drawn at the next frame: call it as often as needed (60 times a second)
|
|
673
|
+
getValue(): number
|
|
674
|
+
setMin(min: number): void; setMax(max: number): void
|
|
675
|
+
setColorStops(colors: string[]): void; setOvershoot(overshoot: number): void // undefined: no overshoot
|
|
676
|
+
refresh(): void // read the css variables again and draw
|
|
677
|
+
}
|
|
678
|
+
// For performance: one canvas, one requestAnimationFrame loop for all the bargraphs (stopped while none moves),
|
|
679
|
+
// a drawing only when a segment changes. 200 x 12 by default (12 x 200 vertical): width / height.
|
|
680
|
+
// The colors are read from the css when it draws first, and again when the theme changed (compared at each
|
|
681
|
+
// drawing), on enable / disable, setColorStops, setOvershoot; a bargraph that does not move keeps its colors
|
|
682
|
+
// until then: refresh() after a change of the theme or of the variables.
|
|
683
|
+
// An unlit segment: its color at --bargraph-off-mix (15%) in --bargraph-off. Disabled: the lit ones in the disabled color.
|
|
684
|
+
// CSS vars: --bargraph-color-start --bargraph-color-end --bargraph-overshoot --bargraph-off --bargraph-off-mix --bargraph-gap
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
```ts
|
|
688
|
+
const meters = channels.map( ( ) => new Bargraph( { vertical: true, overshoot: 85, peak: true } ) )
|
|
689
|
+
audio.onLevels( levels => levels.forEach( ( v, i ) => meters[i].setValue( v ) ) )
|
|
690
|
+
```
|
|
691
|
+
|
|
608
692
|
### `Rating`
|
|
609
693
|
|
|
610
694
|
```ts
|
|
@@ -653,6 +737,56 @@ new Timeline({
|
|
|
653
737
|
})
|
|
654
738
|
```
|
|
655
739
|
|
|
740
|
+
### `MapView` — a map of tiles (openstreetmap), with markers and zones (`x4js/mapview`)
|
|
741
|
+
|
|
742
|
+
```ts
|
|
743
|
+
import { MapView, LatLng, MapMarker } from "x4js/mapview" // not in x4js
|
|
744
|
+
interface LatLng { lat: number; lng: number }
|
|
745
|
+
interface MapMarker extends LatLng { title?: string; icon?: string /* a pin by default */; data?: any }
|
|
746
|
+
type MapShape = // zones, under the markers
|
|
747
|
+
{ type: "polygon"; points: LatLng[] }
|
|
748
|
+
| { type: "circle"; center: LatLng; radius: number /* meters */ }
|
|
749
|
+
| { type: "line"; points: LatLng[] }
|
|
750
|
+
// all: title?: string; color?: string (else --chart-color-1..8 in turn); width?: number (px: 4 a line, 2 a border); data?: any
|
|
751
|
+
|
|
752
|
+
interface MapViewProps extends Omit<ComponentProps, "content"> {
|
|
753
|
+
center?: LatLng; zoom?: number // 0,0 and 2 by default; zoom: whole levels only
|
|
754
|
+
minZoom?: number; maxZoom?: number // 1 and 19 by default
|
|
755
|
+
tiles?: string // url of a tile, {z} {x} {y}: openstreetmap by default
|
|
756
|
+
attribution?: string | UnsafeHtml // the credit of the tiles, always shown: the one of openstreetmap by default
|
|
757
|
+
markers?: MapMarker[]; shapes?: MapShape[]
|
|
758
|
+
move?: EventCallback<EvMapMove> // { center, zoom }: the user moved or zoomed it (not setCenter, setZoom, fitBounds)
|
|
759
|
+
click?: EventCallback<EvMapClick> // { latlng }: a click on the map, not on a marker nor a zone
|
|
760
|
+
markerClick?: EventCallback<EvMarkerClick> // { marker }
|
|
761
|
+
shapeClick?: EventCallback<EvShapeClick> // { shape }
|
|
762
|
+
}
|
|
763
|
+
class MapView extends Component {
|
|
764
|
+
getCenter(): LatLng; setCenter(center: LatLng, zoom?: number): void
|
|
765
|
+
getZoom(): number; setZoom(zoom: number): void
|
|
766
|
+
fitBounds(points: LatLng[], margin = 32): void // the highest zoom where all are seen; before the map has a size: when it gets one
|
|
767
|
+
setMarkers(m: MapMarker[]); addMarker(m); removeMarker(m); getMarkers(): MapMarker[]
|
|
768
|
+
setShapes(s: MapShape[]); addShape(s); removeShape(s); getShapes(): MapShape[]
|
|
769
|
+
}
|
|
770
|
+
// The pointer moves it; the wheel, a double click, the buttons + and −, and the keys + and − zoom it (the point under
|
|
771
|
+
// the pointer stays in place, animated); the arrows move it; two fingers pinch it. East and west, the world goes round.
|
|
772
|
+
// A marker or a zone takes the click from the map; a marker is over a zone. title: a tooltip shown as html (initTooltips;
|
|
773
|
+
// escape a text from a user).
|
|
774
|
+
// openstreetmap is for a light use: an application with many users gives the tiles of a provider (tiles, attribution).
|
|
775
|
+
// No Leaflet: no projection other than web mercator, no fractional zoom, no clusters, no GeoJSON.
|
|
776
|
+
// The dark theme turns the tiles dark (--mapview-tile-filter in themes.scss).
|
|
777
|
+
// CSS vars: --mapview-background --mapview-marker --mapview-tile-filter --mapview-credit-background
|
|
778
|
+
// --mapview-shape-opacity --mapview-shape-opacity-hover
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
```ts
|
|
782
|
+
new MapView({
|
|
783
|
+
height: 400, center: { lat: 48.8566, lng: 2.3522 }, zoom: 13,
|
|
784
|
+
markers: [ { lat: 48.8584, lng: 2.2945, title: 'Tour Eiffel' } ],
|
|
785
|
+
shapes: [ { type: 'circle', center: { lat: 48.8584, lng: 2.2945 }, radius: 500 } ],
|
|
786
|
+
markerClick: ev => this.showPlace(ev.marker),
|
|
787
|
+
})
|
|
788
|
+
```
|
|
789
|
+
|
|
656
790
|
---
|
|
657
791
|
|
|
658
792
|
## Input components
|
|
@@ -702,6 +836,10 @@ type TextEditProps = (TextInputProps | NumberProps | DateProps | TimeProps) & {
|
|
|
702
836
|
class TextEdit extends HBox {
|
|
703
837
|
getValue(): string; setValue(value: string): void; getInput(): Input
|
|
704
838
|
}
|
|
839
|
+
// the class label-top (base.scss): the label above its field, smaller, darker while the field has the focus; on a
|
|
840
|
+
// TextEdit / AutoComplete / Combobox / TextArea, or on a box that holds them (a Form, the main view: all the
|
|
841
|
+
// application). labelWidth is not used then.
|
|
842
|
+
// Vars: --label-top-color / -color-focus / -size / -gap / -gap-textarea, --label-top-suffix (":" after the label)
|
|
705
843
|
```
|
|
706
844
|
|
|
707
845
|
### `TextArea`, `SimpleTextArea` — native `<textarea>`, with or without a label
|
|
@@ -740,9 +878,14 @@ new SimpleTextArea({ flex: 1, placeholder: 'Your message', resize: false })
|
|
|
740
878
|
|
|
741
879
|
```ts
|
|
742
880
|
interface CheckboxProps extends ComponentProps {
|
|
743
|
-
label
|
|
881
|
+
label?: string; icon?: string // no label: the icon alone (give it a tooltip)
|
|
882
|
+
checked?: boolean; value?: boolean|number|string; name?: string
|
|
744
883
|
change?: EventCallback<EvChange>
|
|
745
884
|
}
|
|
885
|
+
// cls: "left" (label on the left), "button" ("button outline" rounded), "danger" (checked in red, its box or its
|
|
886
|
+
// button: "button danger"); without the box, the input is still reached by Tab (--checkbox-button-*, -danger-*)
|
|
887
|
+
// A toggle button is a Checkbox with cls "button": getCheck() tells whether it is pressed, change when it changes
|
|
888
|
+
// new Checkbox( { cls: "button", icon: icon_bold, tooltip: "Bold", change: ( ev ) => this.setBold( ev.value ) } )
|
|
746
889
|
// in a Form: without value (or a boolean one), a boolean; with one, the value when checked, null when not
|
|
747
890
|
class Checkbox extends Component {
|
|
748
891
|
getCheck(): boolean; setCheck(ck: boolean): void
|
|
@@ -766,6 +909,8 @@ class Radio extends Component {
|
|
|
766
909
|
setLabel(text: string): void
|
|
767
910
|
checkValue(vname: string): void // check the radio whose value === vname, in the group of its form (else of the page)
|
|
768
911
|
}
|
|
912
|
+
// cls: "button" (a button; "button outline" rounded), "danger" (checked in red, its round or its button: "button
|
|
913
|
+
// danger"). Without the round, the input is still reached by Tab.
|
|
769
914
|
```
|
|
770
915
|
|
|
771
916
|
### `Switch`
|
|
@@ -774,6 +919,9 @@ class Radio extends Component {
|
|
|
774
919
|
interface SwitchProps extends ComponentProps { label: string; checked?: boolean; value?: string; name?: string }
|
|
775
920
|
// with a name, a Form reads and writes it: a boolean, or with a value, the value when checked and null when not
|
|
776
921
|
class Switch extends HBox {}
|
|
922
|
+
// cls "square": the track and the thumb with corners barely rounded; "danger": the colour of an alert when on
|
|
923
|
+
// its input has role="switch"; its sizes follow the size of <html> (rem), rounded to whole pixels
|
|
924
|
+
// CSS vars: --switch-border --switch-background --switch-background-disabled --switch-checked --switch-danger --switch-thumb
|
|
777
925
|
```
|
|
778
926
|
|
|
779
927
|
### `Slider`
|
|
@@ -791,6 +939,39 @@ class Slider extends Component {
|
|
|
791
939
|
}
|
|
792
940
|
```
|
|
793
941
|
|
|
942
|
+
### `Knob` — a rotary button, on a range or infinite
|
|
943
|
+
|
|
944
|
+
```ts
|
|
945
|
+
interface KnobProps extends ComponentProps {
|
|
946
|
+
value?: number // min by default (0 when infinite)
|
|
947
|
+
min?: number; max?: number // 0 and 100 by default; on an arc of 270°, min at -135°, max at +135° (0 at the top)
|
|
948
|
+
step?: number // 1 by default
|
|
949
|
+
infinite?: boolean // it turns without end: no min nor max, no arc (ticks all around)
|
|
950
|
+
turn?: number // infinite: the value of a whole turn, 360 by default (the value is an angle)
|
|
951
|
+
size?: number // the diameter of the dial in px, 48 by default (width/height size the whole, label included)
|
|
952
|
+
label?: string | UnsafeHtml; icon?: string // under the dial, an x4 Label; none: no room taken
|
|
953
|
+
name?: string // for a Form, which reads a number
|
|
954
|
+
change?: EventCallback<EvChange> // when the user turns it (pointer, wheel, keys), not by the code
|
|
955
|
+
}
|
|
956
|
+
class Knob extends Component {
|
|
957
|
+
getValue(): number; setValue(v: number): void // on the step; on a range, kept between min and max
|
|
958
|
+
setMin(min: number): void; setMax(max: number): void
|
|
959
|
+
setLabel(text: string | UnsafeHtml): void; setIcon(icon: string): void
|
|
960
|
+
}
|
|
961
|
+
// The pointer turns the dial around its center: the knob follows the turn, not the position (no jump when
|
|
962
|
+
// taken; on a range it stops at min and max and comes back at once).
|
|
963
|
+
// The wheel turns it only while it has the focus: the wheel over the page scrolls it.
|
|
964
|
+
// Keys: arrows a step (Ctrl: 10), PageUp / PageDown 10 steps, Home / End min and max (range).
|
|
965
|
+
// infinite: a double click sets it back to its first value (props.value), with a change.
|
|
966
|
+
// CSS vars: --knob-track --knob-value --knob-cap-background --knob-cap-border --knob-pointer --knob-border-focus
|
|
967
|
+
// --knob-track-disabled --knob-value-disabled --knob-cap-background-disabled
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
```ts
|
|
971
|
+
new Knob( { label: "Volume", value: 25, change: ( ev ) => player.setVolume( ev.value ) } )
|
|
972
|
+
new Knob( { infinite: true, turn: 20, change: ( ev ) => list.scrollTo( ev.value ) } ) // an encoder
|
|
973
|
+
```
|
|
974
|
+
|
|
794
975
|
### `Select` — simple native select
|
|
795
976
|
|
|
796
977
|
```ts
|
|
@@ -845,6 +1026,8 @@ class AutoComplete extends TextEdit {
|
|
|
845
1026
|
getSelection(): ListboxID // id of the picked item, undefined for a free text
|
|
846
1027
|
setSelection(item: ListItem): void; showDropDown(): void
|
|
847
1028
|
}
|
|
1029
|
+
// Keys: ↓ (or ↑), the list closed, opens it: the suggestions found last, or else a search of the text as it is
|
|
1030
|
+
// (even empty: minChars is for typing). The list open: ↑ ↓ choose, Enter picks, Escape closes.
|
|
848
1031
|
// Use Combobox for a fixed, known list; AutoComplete when suggestions come from a search (server).
|
|
849
1032
|
// In a Form the value is the field content (text or id, per itemValue).
|
|
850
1033
|
```
|
|
@@ -1034,13 +1217,13 @@ class Store extends CoreElement {
|
|
|
1034
1217
|
}
|
|
1035
1218
|
```
|
|
1036
1219
|
|
|
1037
|
-
### `Chart` — minimal svg chart (line, area, bar, pie, donut)
|
|
1220
|
+
### `Chart` — minimal svg chart (line, area, bar, pie, donut, radar)
|
|
1038
1221
|
|
|
1039
1222
|
```ts
|
|
1040
1223
|
interface ChartProps extends SvgProps {
|
|
1041
|
-
type?: "line" | "area" | "bar" | "pie" | "donut" // default "line"
|
|
1224
|
+
type?: "line" | "area" | "bar" | "pie" | "donut" | "radar" // default "line"
|
|
1042
1225
|
series: ChartSerie[] // required; pie & donut use the first serie
|
|
1043
|
-
labels?: string[] // default: 1, 2, 3
|
|
1226
|
+
labels?: string[] // default: 1, 2, 3...; radar: the name of each axis
|
|
1044
1227
|
xaxis?: ChartAxis; yaxis?: ChartAxis // { name?, ticks?, min?, max?, step? }, all computed by default; on x: max = number of points, step = points between labels (>= 1), min not used
|
|
1045
1228
|
legend?: boolean // default: shown when 2+ named series
|
|
1046
1229
|
stacked?: boolean
|
|
@@ -1053,6 +1236,10 @@ class Chart extends SvgComponent {
|
|
|
1053
1236
|
setTicks(sens: "x" | "y", ticks: ChartTick[]): void
|
|
1054
1237
|
}
|
|
1055
1238
|
// Colors: CSS variables --chart-color-1..8 (or serie.color). Size it with width/height/flex like any component.
|
|
1239
|
+
// radar: an axis for each point (3 at least), the first at the top, clockwise; the center is yaxis.min (0 by
|
|
1240
|
+
// default), the outer ring yaxis.max; a ring for each tick of the y scale (yaxis.step, yaxis.ticks), their
|
|
1241
|
+
// values along the first axis. Each serie is a closed line, tinted inside (serie.fill: its color, "none": the
|
|
1242
|
+
// line only); a value missing is at the center. xaxis.name, yaxis.name and stacked are not used.
|
|
1056
1243
|
// Minimal by design: no dual axis, mixed types, time axis, zoom or animation — use a dedicated library for those.
|
|
1057
1244
|
```
|
|
1058
1245
|
|
|
@@ -1121,6 +1308,8 @@ class Button extends Component {
|
|
|
1121
1308
|
setText(text: string|UnsafeHtml): void
|
|
1122
1309
|
setIcon(icon: string): void
|
|
1123
1310
|
}
|
|
1311
|
+
// A toggle button (pressed / not pressed) is not a Button: it is a Checkbox with cls "button" (see Checkbox),
|
|
1312
|
+
// and one choice among several pressed buttons, Radios with cls "button".
|
|
1124
1313
|
```
|
|
1125
1314
|
|
|
1126
1315
|
### `Tabs`
|
|
@@ -1345,20 +1534,30 @@ class VSplitBox extends VBox // panels stacked, resizable height:
|
|
|
1345
1534
|
|
|
1346
1535
|
// Low level: only place sizers by hand when a SplitBox does not fit (popup edges, header cells...).
|
|
1347
1536
|
class CSizer extends Component // base splitter; events start, resize { size, width, height }, stop
|
|
1348
|
-
class HSizer extends CSizer
|
|
1349
|
-
|
|
1537
|
+
class HSizer extends CSizer { // horizontal splitter, between two children of a HBox
|
|
1538
|
+
constructor(side: "before" | "after" | boolean = "after") // the child it resizes; a boolean (older code): true after, false before
|
|
1539
|
+
}
|
|
1540
|
+
class VSizer extends CSizer { // vertical splitter, between two children of a VBox; same constructor
|
|
1541
|
+
constructor(side: "before" | "after" | boolean = "after")
|
|
1542
|
+
}
|
|
1543
|
+
// new HBox( { content: [ list, new HSizer( "before" ), new VBox( { flex: 1 } ) ] } ) // the list is resized
|
|
1350
1544
|
// A pointercancel (a touch taken by the browser) ends the resize as a pointerup does: stop is fired.
|
|
1351
1545
|
// The columns of a Gridview or a Spreadsheet are 16px wide at least (the min-width of a cell).
|
|
1352
1546
|
class ScrollView extends Component { getViewport(): Component }
|
|
1353
1547
|
class Viewport extends Component
|
|
1354
|
-
class Canvas extends
|
|
1355
|
-
|
|
1548
|
+
class Canvas<P extends CanvasProps = CanvasProps, E extends CanvasEventMap = CanvasEventMap> extends Component<P, E> {
|
|
1549
|
+
getContext(): CanvasRenderingContext2D; redraw(wait?: number): void
|
|
1550
|
+
protected paint(ctx: CanvasEx): void // a derived class draws here, in px of the page (the ratio of the screen is set)
|
|
1551
|
+
}
|
|
1552
|
+
class Video extends Box { // class x4video; props: src? (a file, url), stream? (wins over src), autoplay?, muted?, playsInline?
|
|
1356
1553
|
play(): Promise<boolean> // false: could not start (a click of the user wanted, a source not playable); no source: waits for one
|
|
1357
1554
|
pause(); mute(set: boolean)
|
|
1358
1555
|
get player(): HTMLVideoElement; get duration(): number; get paused(): boolean
|
|
1359
1556
|
get volume(): number; get videoWidth(): number; get videoHeight(): number
|
|
1557
|
+
get src(): string; set src(url: string)
|
|
1360
1558
|
set stream(stream: MediaStream)
|
|
1361
1559
|
}
|
|
1560
|
+
// The image fills the box of the Video, fitted (object-fit: contain): the box keeps its size, whatever the size of the stream.
|
|
1362
1561
|
class Calendar extends VBox { // props: date?, minDate?, maxDate? (days out of them cannot be chosen), change ("change": the day clicked)
|
|
1363
1562
|
getDate(): Date; setDate(date: Date): void // copies: the calendar keeps its own date
|
|
1364
1563
|
}
|
|
@@ -1616,7 +1815,8 @@ isLanguage(name: string): boolean
|
|
|
1616
1815
|
// diff_date_seconds/minutes/hours, date_input_formats, date_format, date_time_format,
|
|
1617
1816
|
// locale ("fr-FR", "en-US", "es-ES", "de-DE": the formats of numbers, amounts, dates),
|
|
1618
1817
|
// day_short[] day_long[] month_short[] month_long[], keyboard.{next,numeric,alpha},
|
|
1619
|
-
// settings.{search,nomatch}, progression.{error,errors,warning,warnings}
|
|
1818
|
+
// settings.{search,nomatch}, progression.{error,errors,warning,warnings},
|
|
1819
|
+
// carousel.{carousel,slide,prev,next,position}
|
|
1620
1820
|
getCurrentLocale(): string; getCurrentCurrency(): string // as Application.getLocale / getCurrency
|
|
1621
1821
|
```
|
|
1622
1822
|
|
|
@@ -1641,8 +1841,12 @@ by the browser, in its own locale, whatever this one.
|
|
|
1641
1841
|
// Each component reads its own variables (the @cssvar of its class), which point to these globals:
|
|
1642
1842
|
// a theme redefines the globals, or one variable of a component.
|
|
1643
1843
|
// Themes: Application.setTheme(name) puts the class x4theme-<name> on <html>. "" is the light theme (by default),
|
|
1644
|
-
// "dark" the dark theme of x4 (the
|
|
1844
|
+
// "dark" the dark theme of x4 (the greys of VS Code Dark Modern, its blues from #5796ca). An application adds its own theme in its css,
|
|
1645
1845
|
// html.x4theme-pink { --color-primary-a0: ...; ... }, then Application.setTheme("pink").
|
|
1846
|
+
// The font: --font-name and --font-size (14px), on :root (normalize.scss); body takes them. To change the size
|
|
1847
|
+
// of the text, set --font-size ( :root { --font-size: 13px } ), not the font-size of <html>: the components
|
|
1848
|
+
// follow --font-size; the font-size of <html> is only the rem of a few inner sizes (the box of a Checkbox, the
|
|
1849
|
+
// round of a Radio, a Switch, a Slider), not the text.
|
|
1646
1850
|
|
|
1647
1851
|
---
|
|
1648
1852
|
|
|
@@ -1704,6 +1908,8 @@ date_format(date, options?): string // options: Intl.DateTimeFormat in the
|
|
|
1704
1908
|
// Formats in the current locale (Application.setLocale, else the one of the language), Intl made once by locale
|
|
1705
1909
|
formatNumber(value, decimals?, grouping = true): string // "1 234,5" in fr-FR; decimals: the most shown
|
|
1706
1910
|
formatMoney(value): string // "1 234,50 €": the currency of Application.getCurrency()
|
|
1911
|
+
formatPercent(value, decimals = 0): string // 89 → "89 %" in fr-FR, "89%" in en-US: a value from 0 to 100
|
|
1912
|
+
formatUnit(value, unit, decimals?): string // short: "12 j", "3 h" in fr-FR, "12 days", "3 hr" in en-US; unit of Intl: "day", "hour"...
|
|
1707
1913
|
formatDate(date): string // "22/10/2026"
|
|
1708
1914
|
formatDateTime(date): string // "22/10/2026 14:05"
|
|
1709
1915
|
// Gridview and Spreadsheet format their columns number, money, date, date-time with them (a formatter comes first)
|
|
@@ -1711,6 +1917,8 @@ date_diff(date1, date2, options?): string // "30 secondes", "5 minutes", "2 he
|
|
|
1711
1917
|
date_to_sql(date, withHours): string; date_sql_utc(date): Date
|
|
1712
1918
|
date_hash(date): number; date_clone(date): Date
|
|
1713
1919
|
date_calc_weeknum(date): number // ISO 8601: weeks from monday, week 1 holds the first thursday (29/12/2025 → 1)
|
|
1920
|
+
date_week_start(date): Date // the monday of its week (itself on a monday), at the same time: a new date
|
|
1921
|
+
date_add_days(date, days): Date // days after (before when < 0), at the same time: a new date; a day of summer time counts as one
|
|
1714
1922
|
parseIntlDate(value, fmts?): Date; formatIntlDate(date, fmt?, utc?): string
|
|
1715
1923
|
calcAge(birth, ref?): number
|
|
1716
1924
|
|
|
@@ -1781,6 +1989,8 @@ class X4PDFBuilder {
|
|
|
1781
1989
|
page(width: number, height: number, callback: (ctx: PageContext) => void): void // points; without size 612 x 792
|
|
1782
1990
|
build(): Uint8Array
|
|
1783
1991
|
buildAsync(): Promise<Uint8Array> // streams compressed (FlateDecode)
|
|
1992
|
+
buildBlob(): Blob // the file, application/pdf (build)
|
|
1993
|
+
buildURL(): string // an object url of it: iframe src, link, window.open; URL.revokeObjectURL frees it
|
|
1784
1994
|
}
|
|
1785
1995
|
export interface PageContext { // origin bottom left, y goes up
|
|
1786
1996
|
text(str, x, y, size, opts?: { align?: 'left'|'center'|'right'; width?; color?; bold? }): void
|
|
@@ -1804,24 +2014,195 @@ measureText(str: string, size: number, bold?: boolean): number // Helvetica
|
|
|
1804
2014
|
|
|
1805
2015
|
---
|
|
1806
2016
|
|
|
2017
|
+
## Components with an entry of their own
|
|
2018
|
+
|
|
2019
|
+
Some components are not in `x4js`: they are big, or seldom used, and an application that does not
|
|
2020
|
+
use them should not carry their code nor their CSS. Each one has an entry of its own in the package:
|
|
2021
|
+
import it from it, not from `x4js` (`import { Gantt } from "x4js"` fails: "has no exported member").
|
|
2022
|
+
|
|
2023
|
+
| Component | Import | More |
|
|
2024
|
+
|---|---|---|
|
|
2025
|
+
| `MonacoEditor` | `import { MonacoEditor } from "x4js/monaco"` | copy its files to the folder `monaco` of the output (below) |
|
|
2026
|
+
| `Gantt` | `import { Gantt } from "x4js/gantt"` | |
|
|
2027
|
+
| `Planner` | `import { Planner } from "x4js/planner"` | |
|
|
2028
|
+
| `MapView` | `import { MapView } from "x4js/mapview"` | `LatLng`, `MapMarker`, `MapShape` from it too |
|
|
2029
|
+
|
|
2030
|
+
---
|
|
2031
|
+
|
|
1807
2032
|
## Code editor (`MonacoEditor`)
|
|
1808
2033
|
|
|
1809
2034
|
Monaco-based code editor component. Monaco is part of the framework surface, so exclude it from `tsc` compilation (see Default project notes).
|
|
1810
2035
|
|
|
2036
|
+
```jsonc
|
|
2037
|
+
// x4.config.json: its files (14 MB) in the folder monaco of the output, where the editor loads them
|
|
2038
|
+
"copy": [ { "from": "./node_modules/x4js/src/components/monaco/bin", "to": "monaco" } ]
|
|
2039
|
+
```
|
|
2040
|
+
|
|
1811
2041
|
```ts
|
|
2042
|
+
import { MonacoEditor } from "x4js/monaco" // not in x4js
|
|
1812
2043
|
interface MonacoEditorProps extends ComponentProps {
|
|
1813
|
-
language: "typescript" | "javascript" | "json" | "css" | "html"
|
|
1814
|
-
theme?: string
|
|
1815
|
-
content?: string
|
|
2044
|
+
language: "typescript" | "javascript" | "json" | "css" | "html" | string
|
|
2045
|
+
theme?: string // a theme of Monaco: "vs", "vs-dark", "hc-black", "hc-light"
|
|
2046
|
+
content?: string // the text at the start
|
|
1816
2047
|
options?: Monaco.editor.IEditorOptions & Monaco.editor.IGlobalEditorOptions
|
|
1817
2048
|
}
|
|
1818
2049
|
class MonacoEditor extends Component<MonacoEditorProps> {
|
|
1819
|
-
constructor(props: MonacoEditorProps)
|
|
1820
|
-
static
|
|
1821
|
-
static
|
|
1822
|
-
|
|
1823
|
-
|
|
2050
|
+
constructor(props: MonacoEditorProps) // the first one loads Monaco: monaco/monaco.js beside the bundle, its workers, its css
|
|
2051
|
+
static addTypelib(name: string, code: string): void // a .d.ts for the completion of typescript; before the first editor too
|
|
2052
|
+
static monaco(): Promise<typeof Monaco> // the module of Monaco, loaded
|
|
2053
|
+
getText(): string; setText(text: string): void
|
|
2054
|
+
setTheme(name: string): void
|
|
2055
|
+
}
|
|
2056
|
+
```
|
|
2057
|
+
|
|
2058
|
+
---
|
|
2059
|
+
|
|
2060
|
+
## `Gantt` — tasks on a calendar (`x4js/gantt`)
|
|
2061
|
+
|
|
2062
|
+
```ts
|
|
2063
|
+
import { Gantt } from "x4js/gantt" // not in x4js
|
|
2064
|
+
type GanttID = string | number
|
|
2065
|
+
type GanttScale = "hour" | "day" | "week" | "month"
|
|
2066
|
+
interface GanttTask {
|
|
2067
|
+
id: GanttID; title: string
|
|
2068
|
+
start?: Date; end?: Date // a date without hours is a whole day: end is its last day, included; none: the day of start
|
|
2069
|
+
progress?: number // 0 to 100
|
|
2070
|
+
milestone?: boolean // a diamond in the middle of its day
|
|
2071
|
+
children?: GanttTask[] // a group: from the first start of its tasks to their last end, its progress weighted by their length
|
|
2072
|
+
open?: boolean // a group: its tasks shown; true by default
|
|
2073
|
+
deps?: GanttID[] // the tasks it waits for: an arrow from the end of each one
|
|
2074
|
+
color?: string // its bar; a group gives it to its tasks that have none; none: the theme
|
|
2075
|
+
tooltip?: string; cls?: string; data?: any
|
|
2076
|
+
}
|
|
2077
|
+
interface GanttColumn {
|
|
2078
|
+
id: string // "title" (the tree), "start", "end", "duration", "progress", or a field of the task
|
|
2079
|
+
title?: string // none: for the ids above, their title in the language (_tr.global.gantt)
|
|
2080
|
+
width?: number; align?: "left" | "center" | "right"; formatter?: (task: GanttTask) => string | UnsafeHtml
|
|
2081
|
+
}
|
|
2082
|
+
interface EvGanttChange extends ComponentEvent { readonly task: GanttTask; readonly start: Date; readonly end: Date; readonly progress: number }
|
|
2083
|
+
interface EvGanttScale extends ComponentEvent { readonly scale: GanttScale }
|
|
2084
|
+
interface GanttProps extends Omit<ComponentProps, "content"> {
|
|
2085
|
+
tasks?: GanttTask[]
|
|
2086
|
+
columns?: GanttColumn[] // the list on the left; the titles alone by default
|
|
2087
|
+
scale?: GanttScale // "day" by default; ctrl+wheel changes it
|
|
2088
|
+
start?: Date; end?: Date // the days shown; else those of the tasks
|
|
2089
|
+
rowHeight?: number // 32 by default
|
|
2090
|
+
readonly?: boolean
|
|
2091
|
+
click?, dblClick?, contextMenu? // ev.context: the task
|
|
2092
|
+
selectionChange?: EventCallback<EvSelectionChange>
|
|
2093
|
+
change?: EventCallback<EvGanttChange> // told before it is written in the task: ev.preventDefault() puts the bar back
|
|
2094
|
+
scaleChange?: EventCallback<EvGanttScale> // the zoom took another scale
|
|
1824
2095
|
}
|
|
2096
|
+
class Gantt extends Component<GanttProps, GanttEvents> {
|
|
2097
|
+
setTasks(tasks: GanttTask[]): void
|
|
2098
|
+
setScale(scale: GanttScale): void // its width of a day; the date at the left stays
|
|
2099
|
+
getScale(): GanttScale
|
|
2100
|
+
setReadOnly(ro: boolean): void
|
|
2101
|
+
selectTask(id: GanttID): void // without selectionChange; null: none
|
|
2102
|
+
getSelection(): GanttID
|
|
2103
|
+
scrollToDate(date: Date): void // in the middle of the chart
|
|
2104
|
+
}
|
|
2105
|
+
// the pointer: moves a bar, stretches it by its ends, its progress by the mark under it (by the hour in "hour", else by the day)
|
|
2106
|
+
// ctrl+wheel on the chart: zoom, the day under the pointer stays; the scale follows the width of a day
|
|
2107
|
+
// keys: up, down, home, end select; right opens a group, left closes it or goes to its group
|
|
2108
|
+
// readonly: no bar moved; disabled (enable(false)): faded as a Box
|
|
2109
|
+
// CSS vars: --gantt-background --gantt-border --gantt-border-focus --gantt-header-background --gantt-header-color
|
|
2110
|
+
// --gantt-line-color --gantt-major-line-color --gantt-weekend-background --gantt-today-color
|
|
2111
|
+
// --gantt-row-background-sel --gantt-row-color-sel --gantt-lane-background-sel --gantt-icon-color
|
|
2112
|
+
// --gantt-bar-background --gantt-bar-border --gantt-bar-progress --gantt-bar-label --gantt-handle-color
|
|
2113
|
+
// --gantt-group-background --gantt-milestone-background --gantt-link-color --gantt-link-color-sel
|
|
2114
|
+
```
|
|
2115
|
+
|
|
2116
|
+
```ts
|
|
2117
|
+
new Gantt({
|
|
2118
|
+
tasks: [
|
|
2119
|
+
{ id: "dev", title: "Development", color: "#fb8c00", children: [
|
|
2120
|
+
{ id: "core", title: "Core", start: new Date(2026, 10, 2), end: new Date(2026, 10, 12), progress: 30 },
|
|
2121
|
+
{ id: "ui", title: "User interface", start: new Date(2026, 10, 6), end: new Date(2026, 10, 18), deps: ["core"] },
|
|
2122
|
+
] },
|
|
2123
|
+
],
|
|
2124
|
+
columns: [{ id: "title", width: 200 }, { id: "start" }, { id: "end" }],
|
|
2125
|
+
change: (ev) => save(ev.task, ev.start, ev.end, ev.progress),
|
|
2126
|
+
})
|
|
2127
|
+
```
|
|
2128
|
+
|
|
2129
|
+
---
|
|
2130
|
+
|
|
2131
|
+
## `Planner` — events by day, week or month (`x4js/planner`)
|
|
2132
|
+
|
|
2133
|
+
```ts
|
|
2134
|
+
import { Planner } from "x4js/planner" // not in x4js
|
|
2135
|
+
type PlannerID = string | number
|
|
2136
|
+
type PlannerView = "day" | "week" | "month"
|
|
2137
|
+
interface PlannerEvent {
|
|
2138
|
+
id: PlannerID; title: string
|
|
2139
|
+
start: Date
|
|
2140
|
+
end?: Date // a date without hours is a whole day: end is its last day, included; else the time it ends.
|
|
2141
|
+
// none: the day of start, or an hour after it
|
|
2142
|
+
text?: string // under the title
|
|
2143
|
+
color?: string // its border, its icon and a tint of its background; none: the accent of the theme
|
|
2144
|
+
icon?: string // on the right of its title, in its colour (as Icon: an svg)
|
|
2145
|
+
tooltip?: string; cls?: string; data?: any
|
|
2146
|
+
}
|
|
2147
|
+
interface EvPlannerChange extends ComponentEvent { readonly event: PlannerEvent; readonly start: Date; readonly end: Date }
|
|
2148
|
+
interface EvPlannerSelect extends ComponentEvent { readonly start: Date; readonly end: Date } // whole days: without hours, end the last one
|
|
2149
|
+
interface EvPlannerView extends ComponentEvent { readonly view: PlannerView; readonly date: Date }
|
|
2150
|
+
interface PlannerProps extends Omit<ComponentProps, "content"> {
|
|
2151
|
+
events?: PlannerEvent[]
|
|
2152
|
+
view?: PlannerView // "week" by default
|
|
2153
|
+
date?: Date // a day shown; today by default
|
|
2154
|
+
workDays?: number[] // as Date.getDay(): monday to friday by default
|
|
2155
|
+
workHours?: [number, number] // minutes from midnight: 8:00 to 18:00 by default
|
|
2156
|
+
showOffDays?: boolean // the other days, narrower; true by default
|
|
2157
|
+
showOffHours?: boolean // the other hours, reached by the scroll; true by default
|
|
2158
|
+
band?: boolean // the whole days in a band under the days; true by default; false: in the working hours
|
|
2159
|
+
showSubGrid?: boolean // the lines of the steps between the hours; false by default
|
|
2160
|
+
step?: number // the precision of the pointer, in minutes: 15 by default
|
|
2161
|
+
readonly?: boolean
|
|
2162
|
+
click?, dblClick?, contextMenu? // ev.context: the event
|
|
2163
|
+
selectionChange?: EventCallback<EvSelectionChange>
|
|
2164
|
+
change?: EventCallback<EvPlannerChange> // moved or stretched; told before it is written in the event: ev.preventDefault() puts it back
|
|
2165
|
+
copy?: EventCallback<EvPlannerChange> // ctrl+drag (option on a Mac): nothing is written, the application adds the copy
|
|
2166
|
+
select?: EventCallback<EvPlannerSelect> // a range of time or of days: the application creates the event
|
|
2167
|
+
viewChange?: EventCallback<EvPlannerView> // a day of the month opened: its week
|
|
2168
|
+
}
|
|
2169
|
+
class Planner extends Component<PlannerProps, PlannerEvents> {
|
|
2170
|
+
setEvents(events: PlannerEvent[]): void // the selection goes when its event is not in them
|
|
2171
|
+
setView(view: PlannerView): void; getView(): PlannerView
|
|
2172
|
+
setDate(date: Date): void; getDate(): Date
|
|
2173
|
+
next(): void; prev(): void // the day, the week, the month after or before
|
|
2174
|
+
getRange(): { start: Date, end: Date } // the first and the last day shown; in the month, its days
|
|
2175
|
+
setBand(band: boolean): void; setShowOffDays(show: boolean): void; setShowOffHours(show: boolean): void
|
|
2176
|
+
setShowSubGrid(show: boolean): void; setStep(step: number): void; setReadOnly(ro: boolean): void
|
|
2177
|
+
selectEvent(id: PlannerID): void // without selectionChange; null: none
|
|
2178
|
+
getSelection(): PlannerID
|
|
2179
|
+
}
|
|
2180
|
+
// the pointer: a drag on an empty place selects a range (shift, or the strip on the right of a day: over the events too);
|
|
2181
|
+
// a drag moves an event (a ghost, the event faded at its place), its top or its bottom stretches it, by step;
|
|
2182
|
+
// an event of one day stays in its day; near the top or the bottom of the view, the view scrolls
|
|
2183
|
+
// the events out of the view: a mark at the top or the bottom of their day, a click on it scrolls to them
|
|
2184
|
+
// a key on the event selected: addShortcut("Delete", ...) and getSelection()
|
|
2185
|
+
// readonly: nothing created, moved, stretched nor copied; disabled (enable(false)): faded as a Box
|
|
2186
|
+
// CSS vars: --planner-background --planner-border --planner-border-focus --planner-header-color --planner-day-color
|
|
2187
|
+
// --planner-off-color --planner-today-background --planner-today-color --planner-today-column
|
|
2188
|
+
// --planner-off-background --planner-line-color --planner-step-color --planner-hour-line-color
|
|
2189
|
+
// --planner-hour-color --planner-hour-color-work --planner-edge-background --planner-edge-color
|
|
2190
|
+
// --planner-edge-shadow --planner-edge-opacity --planner-now-color --planner-now-tag --planner-now-text
|
|
2191
|
+
// --planner-event-background --planner-event-color --planner-event-tint --planner-event-radius
|
|
2192
|
+
// --planner-event-title --planner-event-text --planner-event-selected --planner-range-background
|
|
2193
|
+
// --planner-range-border --planner-out-color --planner-more-color
|
|
2194
|
+
```
|
|
2195
|
+
|
|
2196
|
+
```ts
|
|
2197
|
+
const planner = new Planner({
|
|
2198
|
+
events: [
|
|
2199
|
+
{ id: 1, title: "Design review", start: new Date(2026, 9, 26, 10), end: new Date(2026, 9, 26, 12), color: "#2e9e48" },
|
|
2200
|
+
{ id: 2, title: "Day off", start: new Date(2026, 9, 30) },
|
|
2201
|
+
],
|
|
2202
|
+
select: (ev) => create(ev.start, ev.end),
|
|
2203
|
+
change: (ev) => save(ev.event, ev.start, ev.end),
|
|
2204
|
+
})
|
|
2205
|
+
planner.addShortcut("Delete", () => remove(planner.getSelection()))
|
|
1825
2206
|
```
|
|
1826
2207
|
|
|
1827
2208
|
---
|
|
@@ -1839,6 +2220,13 @@ createPainter(c2d: CanvasRenderingContext2D, w: number, h: number): CanvasEx /
|
|
|
1839
2220
|
initTooltips(): void // initialize the global tooltip system
|
|
1840
2221
|
```
|
|
1841
2222
|
|
|
2223
|
+
JSX, in `.tsx` files: in tsconfig.json `"jsx": "react"` and `"jsxFactory": "x4_react.create_element"` (esbuild reads
|
|
2224
|
+
them), and `"files": [ "node_modules/x4js/src/x4tsx.d.ts" ]` for the JSX types (`ref`); each `.tsx` file imports `x4_react`.
|
|
2225
|
+
`<X a={1}>{kids}</X>` is `new X( { a: 1, content: kids } )`; a tag in lower case is an html element (its attributes set,
|
|
2226
|
+
an attribute `on...` an event); arrays in braces are flattened, `null`, `undefined` and `false` give nothing (`setContent`
|
|
2227
|
+
does not flatten nested arrays: build such a content in JSX); `ref={[owner, "name"]}` keeps the element in
|
|
2228
|
+
`owner.refs.name`, the owner a Box.
|
|
2229
|
+
|
|
1842
2230
|
No tooltip is shown until the application calls `initTooltips()` once (the prop `tooltip` only sets an attribute).
|
|
1843
2231
|
`initTooltips()` shows the text of the attribute `tooltip` (the prop `tooltip`), or the one `getTip()`
|
|
1844
2232
|
of a `"tip-handler"` gives, **as html**. A text that comes from a user is escaped by the application
|
|
@@ -2078,7 +2466,7 @@ Optional keys: `external`, `define`, `esbuild`, `dev` (`host`, `port`, `https`,
|
|
|
2078
2466
|
- The default language is `fr`. Call `selectLanguage('en')` to switch to English.
|
|
2079
2467
|
- `Gridview` is backed by a `DataStore`; `Spreadsheet` is backed by a `Store` (different, cell-based).
|
|
2080
2468
|
- `DataRecordID` is currently typed `any`; `strictNullChecks` is intentionally off in the recommended `tsconfig.json`.
|
|
2081
|
-
- `MonacoEditor`
|
|
2082
|
-
- `x4_react.create_element` is the JSX pragma
|
|
2469
|
+
- `MonacoEditor` loads Monaco by itself (the first editor), from the folder `monaco` of the output where its files are copied; register extra typings with `MonacoEditor.addTypelib(name, code)`.
|
|
2470
|
+
- `x4_react.create_element` is the JSX pragma (the setup: JSX factory & helpers).
|
|
2083
2471
|
- A DOM handler of an ancestor is called for the events of its descendants, as in the browser. Not for the events that do not bubble: `focus`, `blur`, `mouseenter`, `mouseleave` (use `focusin` / `focusout`, `mouseover` / `mouseout`), nor for those of x4: `created`, `removed`, `resized`.
|
|
2084
2472
|
- Several handlers of one event on one node are called in the order they were added. One that returns `true` stops those that follow it.
|