@xenosystem/components 0.10.2 → 0.11.1

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/dist/catalog.json CHANGED
@@ -937,6 +937,256 @@
937
937
  }
938
938
  ]
939
939
  },
940
+ {
941
+ "name": "Input",
942
+ "file": "controls.tsx",
943
+ "doc": "A single-line text field — the inspector's cell, a name, a path. Mixed and read-only are treatments, not colours.",
944
+ "props": "InputProps",
945
+ "propList": [
946
+ {
947
+ "name": "value",
948
+ "type": "string",
949
+ "optional": false,
950
+ "doc": ""
951
+ },
952
+ {
953
+ "name": "onChange",
954
+ "type": "(value: string) => void",
955
+ "optional": true,
956
+ "doc": ""
957
+ },
958
+ {
959
+ "name": "mixed",
960
+ "type": "boolean",
961
+ "optional": true,
962
+ "doc": "The value differs across a multi-selection: the field shows the host's mixed placeholder in the\nquiet italic form, and clears on the first keystroke. A read of `value` is empty then."
963
+ },
964
+ {
965
+ "name": "readOnly",
966
+ "type": "boolean",
967
+ "optional": true,
968
+ "doc": "Read-only renders without a well — a value shown, not a field offered."
969
+ },
970
+ {
971
+ "name": "variant",
972
+ "type": "'text' | 'mono'",
973
+ "optional": true,
974
+ "doc": "`mono` for code-like values (an id, a path)."
975
+ },
976
+ {
977
+ "name": "label",
978
+ "type": "string",
979
+ "optional": true,
980
+ "doc": "Accessible name."
981
+ },
982
+ {
983
+ "name": "className",
984
+ "type": "string",
985
+ "optional": true,
986
+ "doc": ""
987
+ }
988
+ ]
989
+ },
990
+ {
991
+ "name": "TextArea",
992
+ "file": "controls.tsx",
993
+ "doc": "A multi-line field for prose or code. The composer is a different unit: it has a submit convention; this has none.",
994
+ "props": "TextAreaProps",
995
+ "propList": [
996
+ {
997
+ "name": "value",
998
+ "type": "string",
999
+ "optional": false,
1000
+ "doc": ""
1001
+ },
1002
+ {
1003
+ "name": "onChange",
1004
+ "type": "(value: string) => void",
1005
+ "optional": true,
1006
+ "doc": ""
1007
+ },
1008
+ {
1009
+ "name": "mixed",
1010
+ "type": "boolean",
1011
+ "optional": true,
1012
+ "doc": ""
1013
+ },
1014
+ {
1015
+ "name": "readOnly",
1016
+ "type": "boolean",
1017
+ "optional": true,
1018
+ "doc": ""
1019
+ },
1020
+ {
1021
+ "name": "variant",
1022
+ "type": "'text' | 'mono'",
1023
+ "optional": true,
1024
+ "doc": "`mono` for code — deliberately a plain textarea, never an embedded editor."
1025
+ },
1026
+ {
1027
+ "name": "label",
1028
+ "type": "string",
1029
+ "optional": true,
1030
+ "doc": ""
1031
+ },
1032
+ {
1033
+ "name": "className",
1034
+ "type": "string",
1035
+ "optional": true,
1036
+ "doc": ""
1037
+ }
1038
+ ]
1039
+ },
1040
+ {
1041
+ "name": "Select",
1042
+ "file": "controls.tsx",
1043
+ "doc": "A closed choice. ⚠️ The popup is the browser's — Chrome paints it white and no CSS reaches it, the\nhazard every dark XENO surface has met; the trigger is ours. A page-content listbox (the\nextension's `xeno-select.js`) is the next form of this unit, with the same props.",
1044
+ "props": "SelectProps",
1045
+ "propList": [
1046
+ {
1047
+ "name": "options",
1048
+ "type": "ReadonlyArray<SelectOption<T>>",
1049
+ "optional": false,
1050
+ "doc": ""
1051
+ },
1052
+ {
1053
+ "name": "value",
1054
+ "type": "T | ''",
1055
+ "optional": false,
1056
+ "doc": ""
1057
+ },
1058
+ {
1059
+ "name": "onChange",
1060
+ "type": "(value: T) => void",
1061
+ "optional": false,
1062
+ "doc": ""
1063
+ },
1064
+ {
1065
+ "name": "mixed",
1066
+ "type": "boolean",
1067
+ "optional": true,
1068
+ "doc": ""
1069
+ },
1070
+ {
1071
+ "name": "disabled",
1072
+ "type": "boolean",
1073
+ "optional": true,
1074
+ "doc": ""
1075
+ },
1076
+ {
1077
+ "name": "label",
1078
+ "type": "string",
1079
+ "optional": true,
1080
+ "doc": ""
1081
+ },
1082
+ {
1083
+ "name": "className",
1084
+ "type": "string",
1085
+ "optional": true,
1086
+ "doc": ""
1087
+ }
1088
+ ]
1089
+ },
1090
+ {
1091
+ "name": "Swatch",
1092
+ "file": "controls.tsx",
1093
+ "doc": "A colour chip with its value beside it. Empty and mixed draw an outlined, transparent chip.",
1094
+ "props": "SwatchProps",
1095
+ "propList": [
1096
+ {
1097
+ "name": "color",
1098
+ "type": "string",
1099
+ "optional": true,
1100
+ "doc": "A CSS colour, or absent for \"none\". The unit never parses it."
1101
+ },
1102
+ {
1103
+ "name": "text",
1104
+ "type": "ReactNode",
1105
+ "optional": true,
1106
+ "doc": "The text beside the chip — the hex, a token name; the host's mixed placeholder when `mixed`."
1107
+ },
1108
+ {
1109
+ "name": "mixed",
1110
+ "type": "boolean",
1111
+ "optional": true,
1112
+ "doc": ""
1113
+ },
1114
+ {
1115
+ "name": "onClick",
1116
+ "type": "() => void",
1117
+ "optional": true,
1118
+ "doc": "Clicking opens the host's colour editor — the swatch never edits in place."
1119
+ },
1120
+ {
1121
+ "name": "disabled",
1122
+ "type": "boolean",
1123
+ "optional": true,
1124
+ "doc": ""
1125
+ },
1126
+ {
1127
+ "name": "label",
1128
+ "type": "string",
1129
+ "optional": false,
1130
+ "doc": "Accessible name, e.g. `Fill: white`."
1131
+ },
1132
+ {
1133
+ "name": "title",
1134
+ "type": "string",
1135
+ "optional": true,
1136
+ "doc": ""
1137
+ },
1138
+ {
1139
+ "name": "className",
1140
+ "type": "string",
1141
+ "optional": true,
1142
+ "doc": ""
1143
+ }
1144
+ ]
1145
+ },
1146
+ {
1147
+ "name": "AxisLabel",
1148
+ "file": "controls.tsx",
1149
+ "doc": "The one-letter axis tag before a numeric cell — and the scrub handle when the caller wires one.",
1150
+ "props": "AxisLabelProps",
1151
+ "propList": [
1152
+ {
1153
+ "name": "children",
1154
+ "type": "ReactNode",
1155
+ "optional": false,
1156
+ "doc": "`X`, `Y`, `W`, `H`, `R`…"
1157
+ },
1158
+ {
1159
+ "name": "scrubbable",
1160
+ "type": "boolean",
1161
+ "optional": true,
1162
+ "doc": "Draggable labels scrub the value beside them (Blender / Figma); the gesture maths is the caller's."
1163
+ },
1164
+ {
1165
+ "name": "onPointerDown",
1166
+ "type": "(e: PointerEvent<HTMLSpanElement>) => void",
1167
+ "optional": true,
1168
+ "doc": ""
1169
+ },
1170
+ {
1171
+ "name": "onPointerMove",
1172
+ "type": "(e: PointerEvent<HTMLSpanElement>) => void",
1173
+ "optional": true,
1174
+ "doc": ""
1175
+ },
1176
+ {
1177
+ "name": "onPointerUp",
1178
+ "type": "(e: PointerEvent<HTMLSpanElement>) => void",
1179
+ "optional": true,
1180
+ "doc": ""
1181
+ },
1182
+ {
1183
+ "name": "className",
1184
+ "type": "string",
1185
+ "optional": true,
1186
+ "doc": ""
1187
+ }
1188
+ ]
1189
+ },
940
1190
  {
941
1191
  "name": "PanelFrame",
942
1192
  "file": "frame.tsx",
@@ -1,4 +1,4 @@
1
- import { ReactNode, HTMLAttributes, PointerEvent, KeyboardEvent, Ref, ReactElement } from 'react';
1
+ import { ReactNode, HTMLAttributes, PointerEvent, KeyboardEvent, Ref, InputHTMLAttributes, TextareaHTMLAttributes, ReactElement } from 'react';
2
2
 
3
3
  /**
4
4
  * Frame + strip primitives: `PanelFrame`, `Toolbar`, `ToolbarGroup`, `Divider`, `ScrollArea`.
@@ -778,6 +778,90 @@ interface SliderProps {
778
778
  }
779
779
  /** A horizontal slider: track, fill and knob. The playhead scrubber; a zoom; a level. */
780
780
  declare function Slider({ progress, label, valueMin, valueMax, valueNow, valueText, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onKeyDown, disabled, className, trackRef }: SliderProps): ReactNode;
781
+ /** Props for {@link Input}. Everything an inspector cell needs and nothing an inspector owns (gestures stay in the block). */
782
+ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'size'> {
783
+ value: string;
784
+ onChange?: (value: string) => void;
785
+ /**
786
+ * The value differs across a multi-selection: the field shows the host's mixed placeholder in the
787
+ * quiet italic form, and clears on the first keystroke. A read of `value` is empty then.
788
+ */
789
+ mixed?: boolean;
790
+ /** Read-only renders without a well — a value shown, not a field offered. */
791
+ readOnly?: boolean;
792
+ /** `mono` for code-like values (an id, a path). */
793
+ variant?: 'text' | 'mono';
794
+ /** Accessible name. */
795
+ label?: string;
796
+ className?: string;
797
+ }
798
+ /** A single-line text field — the inspector's cell, a name, a path. Mixed and read-only are treatments, not colours. */
799
+ declare function Input({ value, onChange, mixed, readOnly, variant, label, className, disabled, ...rest }: InputProps): ReactNode;
800
+ /** Props for {@link TextArea}. */
801
+ interface TextAreaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'onChange'> {
802
+ value: string;
803
+ onChange?: (value: string) => void;
804
+ mixed?: boolean;
805
+ readOnly?: boolean;
806
+ /** `mono` for code — deliberately a plain textarea, never an embedded editor. */
807
+ variant?: 'text' | 'mono';
808
+ label?: string;
809
+ className?: string;
810
+ }
811
+ /** A multi-line field for prose or code. The composer is a different unit: it has a submit convention; this has none. */
812
+ declare function TextArea({ value, onChange, mixed, readOnly, variant, label, className, ...rest }: TextAreaProps): ReactNode;
813
+ /** One option of a {@link Select}. */
814
+ interface SelectOption<T extends string> {
815
+ value: T;
816
+ label: ReactNode;
817
+ disabled?: boolean;
818
+ }
819
+ /** Props for {@link Select}. */
820
+ interface SelectProps<T extends string> {
821
+ options: ReadonlyArray<SelectOption<T>>;
822
+ value: T | '';
823
+ onChange: (value: T) => void;
824
+ mixed?: boolean;
825
+ disabled?: boolean;
826
+ label?: string;
827
+ className?: string;
828
+ }
829
+ /**
830
+ * A closed choice. ⚠️ The popup is the browser's — Chrome paints it white and no CSS reaches it, the
831
+ * hazard every dark XENO surface has met; the trigger is ours. A page-content listbox (the
832
+ * extension's `xeno-select.js`) is the next form of this unit, with the same props.
833
+ */
834
+ declare function Select<T extends string>({ options, value, onChange, mixed, disabled, label, className }: SelectProps<T>): ReactNode;
835
+ /** Props for {@link Swatch}. */
836
+ interface SwatchProps {
837
+ /** A CSS colour, or absent for "none". The unit never parses it. */
838
+ color?: string;
839
+ /** The text beside the chip — the hex, a token name; the host's mixed placeholder when `mixed`. */
840
+ text?: ReactNode;
841
+ mixed?: boolean;
842
+ /** Clicking opens the host's colour editor — the swatch never edits in place. */
843
+ onClick?: () => void;
844
+ disabled?: boolean;
845
+ /** Accessible name, e.g. `Fill: white`. */
846
+ label: string;
847
+ title?: string;
848
+ className?: string;
849
+ }
850
+ /** A colour chip with its value beside it. Empty and mixed draw an outlined, transparent chip. */
851
+ declare function Swatch({ color, text, mixed, onClick, disabled, label, title, className }: SwatchProps): ReactNode;
852
+ /** Props for {@link AxisLabel}. */
853
+ interface AxisLabelProps {
854
+ /** `X`, `Y`, `W`, `H`, `R`… */
855
+ children: ReactNode;
856
+ /** Draggable labels scrub the value beside them (Blender / Figma); the gesture maths is the caller's. */
857
+ scrubbable?: boolean;
858
+ onPointerDown?: (e: PointerEvent<HTMLSpanElement>) => void;
859
+ onPointerMove?: (e: PointerEvent<HTMLSpanElement>) => void;
860
+ onPointerUp?: (e: PointerEvent<HTMLSpanElement>) => void;
861
+ className?: string;
862
+ }
863
+ /** The one-letter axis tag before a numeric cell — and the scrub handle when the caller wires one. */
864
+ declare function AxisLabel({ children, scrubbable, onPointerDown, onPointerMove, onPointerUp, className }: AxisLabelProps): ReactNode;
781
865
 
782
866
  /**
783
867
  * The three terminal states: `EmptyState`, `LoadingState`, `ErrorState`.
@@ -1261,6 +1345,11 @@ declare const COMPONENTS: {
1261
1345
  readonly 'chrome/Readout': typeof Readout;
1262
1346
  readonly 'chrome/NumberInput': typeof NumberInput;
1263
1347
  readonly 'chrome/Slider': typeof Slider;
1348
+ readonly 'chrome/Input': typeof Input;
1349
+ readonly 'chrome/TextArea': typeof TextArea;
1350
+ readonly 'chrome/Select': typeof Select;
1351
+ readonly 'chrome/Swatch': typeof Swatch;
1352
+ readonly 'chrome/AxisLabel': typeof AxisLabel;
1264
1353
  readonly 'chrome/SegmentedControl': typeof SegmentedControl;
1265
1354
  readonly 'chrome/Toggle': typeof Toggle;
1266
1355
  readonly 'chrome/FieldGroup': typeof FieldGroup;
@@ -1311,4 +1400,4 @@ interface GalleryEntry {
1311
1400
  /** The entries, one per unit × state. Adding a unit to {@link COMPONENTS} without an entry here fails the family's test. */
1312
1401
  declare function gallery(): GalleryEntry[];
1313
1402
 
1314
- export { Badge, type BadgeProps, CLS_BADGE, CLS_BAR, CLS_BAR_SEGMENT, CLS_DIVIDER, CLS_DOT, CLS_EMPTY, CLS_ERROR, CLS_FIELD, CLS_FIELD_CONTROL, CLS_FIELD_GROUP, CLS_FIELD_HINT, CLS_FIELD_LABEL, CLS_ICON_BUTTON, CLS_ICON_FRAME, CLS_LOADING, CLS_PANEL, CLS_PANEL_CONTENT, CLS_PANEL_CONTENT_NO_HEADER, CLS_PANEL_HEADER, CLS_PANEL_TITLE, CLS_ROW, CLS_ROWLIST, CLS_ROW_ICON, CLS_ROW_LABEL, CLS_ROW_META, CLS_ROW_TRAILING, CLS_SCROLL, CLS_SEARCH, CLS_SEARCH_INPUT, CLS_SECTION, CLS_SECTION_ACTIONS, CLS_SECTION_BODY, CLS_SECTION_CHEVRON, CLS_SECTION_HEADER, CLS_SECTION_TITLE, CLS_SECTION_TRIGGER, CLS_SEGMENT, CLS_SEGMENTED, CLS_STAT, CLS_STATE, CLS_STATE_HINT, CLS_STATE_ICON, CLS_STATE_PROGRESS, CLS_STATE_TITLE, CLS_STATUSBAR, CLS_STAT_LABEL, CLS_STAT_SUB, CLS_STAT_VALUE, CLS_TEXT_BUTTON, CLS_TOGGLE, CLS_TOGGLE_KNOB, CLS_TOOLBAR, CLS_TOOLBAR_GROUP, COMPONENTS, ChevronGlyph, ClearGlyph, Column, type ColumnProps, Composer, type ComposerProps, Divider, type DividerProps, EmptyState, type EmptyStateProps, ErrorState, type ErrorStateProps, Field, FieldGroup, type FieldGroupProps, type FieldProps, type GalleryEntry, IconButton, type IconButtonProps, IconFrame, type IconFrameProps, LoadingState, type LoadingStateProps, NumberInput, type NumberInputProps, PANEL_COLORS, PANEL_DISABLED_OPACITY, PANEL_METRICS, PANEL_PRIMITIVES_CSS, PANEL_PRIMITIVES_STYLE_ID, PANEL_TONES, type PanelColors, PanelFrame, type PanelFrameProps, type PanelMetrics, type PanelTone, type PrimitivesInjectTarget, ProportionBar, type ProportionBarProps, type ProportionSegment, Readout, type ReadoutProps, Row, RowList, type RowListProps, type RowProps, type RowState, ScrollArea, type ScrollAreaProps, SearchField, type SearchFieldProps, SearchGlyph, Section, type SectionProps, type SegmentOption, SegmentedControl, type SegmentedControlProps, Slider, type SliderProps, Sparkline, type SparklineProps, Stack, type StackProps, StatTile, type StatTileProps, StatusBadge, type StatusBadgeProps, StatusBar, type StatusBarProps, StatusDot, type StatusDotProps, Text, TextButton, type TextButtonProps, type TextProps, Toggle, type ToggleProps, Toolbar, ToolbarGroup, type ToolbarGroupProps, type ToolbarProps, badgeClass, composerWantsSubmit, cx, dotClass, ensurePanelPrimitivesInjected, gallery, iconButtonClass, nextSegmentValue, rowClass, sectionClass, usePanelPrimitives };
1403
+ export { AxisLabel, type AxisLabelProps, Badge, type BadgeProps, CLS_BADGE, CLS_BAR, CLS_BAR_SEGMENT, CLS_DIVIDER, CLS_DOT, CLS_EMPTY, CLS_ERROR, CLS_FIELD, CLS_FIELD_CONTROL, CLS_FIELD_GROUP, CLS_FIELD_HINT, CLS_FIELD_LABEL, CLS_ICON_BUTTON, CLS_ICON_FRAME, CLS_LOADING, CLS_PANEL, CLS_PANEL_CONTENT, CLS_PANEL_CONTENT_NO_HEADER, CLS_PANEL_HEADER, CLS_PANEL_TITLE, CLS_ROW, CLS_ROWLIST, CLS_ROW_ICON, CLS_ROW_LABEL, CLS_ROW_META, CLS_ROW_TRAILING, CLS_SCROLL, CLS_SEARCH, CLS_SEARCH_INPUT, CLS_SECTION, CLS_SECTION_ACTIONS, CLS_SECTION_BODY, CLS_SECTION_CHEVRON, CLS_SECTION_HEADER, CLS_SECTION_TITLE, CLS_SECTION_TRIGGER, CLS_SEGMENT, CLS_SEGMENTED, CLS_STAT, CLS_STATE, CLS_STATE_HINT, CLS_STATE_ICON, CLS_STATE_PROGRESS, CLS_STATE_TITLE, CLS_STATUSBAR, CLS_STAT_LABEL, CLS_STAT_SUB, CLS_STAT_VALUE, CLS_TEXT_BUTTON, CLS_TOGGLE, CLS_TOGGLE_KNOB, CLS_TOOLBAR, CLS_TOOLBAR_GROUP, COMPONENTS, ChevronGlyph, ClearGlyph, Column, type ColumnProps, Composer, type ComposerProps, Divider, type DividerProps, EmptyState, type EmptyStateProps, ErrorState, type ErrorStateProps, Field, FieldGroup, type FieldGroupProps, type FieldProps, type GalleryEntry, IconButton, type IconButtonProps, IconFrame, type IconFrameProps, Input, type InputProps, LoadingState, type LoadingStateProps, NumberInput, type NumberInputProps, PANEL_COLORS, PANEL_DISABLED_OPACITY, PANEL_METRICS, PANEL_PRIMITIVES_CSS, PANEL_PRIMITIVES_STYLE_ID, PANEL_TONES, type PanelColors, PanelFrame, type PanelFrameProps, type PanelMetrics, type PanelTone, type PrimitivesInjectTarget, ProportionBar, type ProportionBarProps, type ProportionSegment, Readout, type ReadoutProps, Row, RowList, type RowListProps, type RowProps, type RowState, ScrollArea, type ScrollAreaProps, SearchField, type SearchFieldProps, SearchGlyph, Section, type SectionProps, type SegmentOption, SegmentedControl, type SegmentedControlProps, Select, type SelectOption, type SelectProps, Slider, type SliderProps, Sparkline, type SparklineProps, Stack, type StackProps, StatTile, type StatTileProps, StatusBadge, type StatusBadgeProps, StatusBar, type StatusBarProps, StatusDot, type StatusDotProps, Swatch, type SwatchProps, Text, TextArea, type TextAreaProps, TextButton, type TextButtonProps, type TextProps, Toggle, type ToggleProps, Toolbar, ToolbarGroup, type ToolbarGroupProps, type ToolbarProps, badgeClass, composerWantsSubmit, cx, dotClass, ensurePanelPrimitivesInjected, gallery, iconButtonClass, nextSegmentValue, rowClass, sectionClass, usePanelPrimitives };
@@ -59,6 +59,9 @@ var CLS_TEXT = "xc-text";
59
59
  var CLS_READOUT = "xc-readout";
60
60
  var CLS_NUMBER = "xc-number";
61
61
  var CLS_SLIDER = "xc-slider";
62
+ var CLS_INPUT = "xc-input";
63
+ var CLS_SWATCH = "xc-swatch";
64
+ var CLS_AXIS = "xc-axis";
62
65
  var PANEL_TONES = Object.freeze([
63
66
  "neutral",
64
67
  "info",
@@ -679,6 +682,10 @@ div.xc-section-trigger {
679
682
  .xc-stack--pad-gutter { padding: ${PANEL_METRICS.gutter}px; }
680
683
  .xc-stack--pad-inset { padding: 0 ${PANEL_METRICS.gutter}px ${PANEL_METRICS.gutter}px; }
681
684
  .xc-stack--pad-none { padding: 0; }
685
+ /* inside a row, a field shares the line instead of claiming it \u2014 the inspector's X / Y cells */
686
+ .xc-stack--row > .xc-input,
687
+ .xc-stack--row > .xc-number,
688
+ .xc-stack--row > .xc-swatch { flex: 1 1 0; width: auto; min-width: 0; }
682
689
 
683
690
  /* \u2500\u2500 Text \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
684
691
  .xc-text {
@@ -826,6 +833,118 @@ div.xc-section-trigger {
826
833
  }
827
834
 
828
835
 
836
+ /* \u2500\u2500 Input / TextArea / Select \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
837
+ .xc-input {
838
+ display: block;
839
+ width: 100%;
840
+ min-width: 0;
841
+ height: ${PANEL_METRICS.controlHeightSm}px;
842
+ box-sizing: border-box;
843
+ padding: 0 4px;
844
+ border: 1px solid ${BORDER};
845
+ border-radius: ${PANEL_METRICS.radius}px;
846
+ background: ${PANEL_COLORS.well};
847
+ color: ${FG};
848
+ font: inherit;
849
+ font-size: ${PANEL_METRICS.fontBody}px;
850
+ outline: none;
851
+ }
852
+ .xc-input::placeholder {
853
+ color: ${DIM};
854
+ }
855
+ .xc-input:focus-visible {
856
+ border-color: ${PANEL_COLORS.ring};
857
+ }
858
+ .xc-input:disabled {
859
+ opacity: ${PANEL_DISABLED_OPACITY};
860
+ cursor: not-allowed;
861
+ }
862
+ /* a value that differs across the selection: the quiet italic form, never a colour of its own */
863
+ .xc-input--mixed,
864
+ .xc-input--mixed::placeholder {
865
+ font-style: italic;
866
+ color: ${MUTED};
867
+ }
868
+ /* shown, not offered */
869
+ .xc-input--readonly {
870
+ background: transparent;
871
+ border-color: transparent;
872
+ cursor: default;
873
+ }
874
+ .xc-input--mono {
875
+ font-family: var(--xeno-font-mono);
876
+ font-size: ${PANEL_METRICS.fontMeta}px;
877
+ }
878
+ .xc-input--area {
879
+ height: auto;
880
+ min-height: 34px;
881
+ padding: 3px 4px;
882
+ resize: vertical;
883
+ line-height: 1.4;
884
+ }
885
+ .xc-input--select {
886
+ appearance: none;
887
+ padding-right: 14px;
888
+ cursor: pointer;
889
+ }
890
+
891
+ /* \u2500\u2500 Swatch \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
892
+ .xc-swatch {
893
+ display: inline-flex;
894
+ align-items: center;
895
+ gap: 5px;
896
+ min-width: 0;
897
+ height: ${PANEL_METRICS.controlHeightSm}px;
898
+ padding: 0 4px;
899
+ border: 1px solid transparent;
900
+ border-radius: ${PANEL_METRICS.radius}px;
901
+ background: transparent;
902
+ color: ${FG};
903
+ font: inherit;
904
+ cursor: pointer;
905
+ }
906
+ .xc-swatch:hover {
907
+ border-color: ${BORDER};
908
+ }
909
+ .xc-swatch:focus-visible {
910
+ outline: 1px solid ${PANEL_COLORS.focus};
911
+ outline-offset: -1px;
912
+ }
913
+ .xc-swatch:disabled {
914
+ opacity: ${PANEL_DISABLED_OPACITY};
915
+ cursor: default;
916
+ }
917
+ .xc-swatch-chip {
918
+ width: 10px;
919
+ height: 10px;
920
+ flex: 0 0 auto;
921
+ border-radius: ${PANEL_METRICS.radius}px;
922
+ border: 1px solid ${BORDER};
923
+ }
924
+ .xc-swatch-chip--empty {
925
+ background: transparent;
926
+ }
927
+ .xc-swatch-text {
928
+ font-size: ${PANEL_METRICS.fontMeta}px;
929
+ font-variant-numeric: tabular-nums;
930
+ overflow: hidden;
931
+ text-overflow: ellipsis;
932
+ white-space: nowrap;
933
+ }
934
+
935
+ /* \u2500\u2500 AxisLabel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
936
+ .xc-axis {
937
+ flex: 0 0 auto;
938
+ width: 9px;
939
+ text-align: center;
940
+ font-size: ${PANEL_METRICS.fontMeta - 1}px;
941
+ color: ${MUTED};
942
+ user-select: none;
943
+ }
944
+ .xc-axis--scrub {
945
+ cursor: ew-resize;
946
+ }
947
+
829
948
  /* \u2500\u2500 Composer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
830
949
  .xc-composer {
831
950
  display: block;
@@ -1704,6 +1823,77 @@ function Slider({ progress, label, valueMin = 0, valueMax = 1, valueNow, valueTe
1704
1823
  }
1705
1824
  );
1706
1825
  }
1826
+ function Input({ value, onChange, mixed, readOnly, variant = "text", label, className, disabled, ...rest }) {
1827
+ usePanelPrimitives();
1828
+ return /* @__PURE__ */ jsx2(
1829
+ "input",
1830
+ {
1831
+ type: "text",
1832
+ ...rest,
1833
+ className: cx(CLS_INPUT, mixed && `${CLS_INPUT}--mixed`, readOnly && `${CLS_INPUT}--readonly`, variant === "mono" && `${CLS_INPUT}--mono`, className),
1834
+ value,
1835
+ readOnly,
1836
+ disabled,
1837
+ "aria-label": label ?? rest["aria-label"],
1838
+ spellCheck: rest.spellCheck ?? false,
1839
+ onChange: (e) => onChange?.(e.target.value)
1840
+ }
1841
+ );
1842
+ }
1843
+ function TextArea({ value, onChange, mixed, readOnly, variant = "text", label, className, ...rest }) {
1844
+ usePanelPrimitives();
1845
+ return /* @__PURE__ */ jsx2(
1846
+ "textarea",
1847
+ {
1848
+ ...rest,
1849
+ className: cx(CLS_INPUT, `${CLS_INPUT}--area`, mixed && `${CLS_INPUT}--mixed`, readOnly && `${CLS_INPUT}--readonly`, variant === "mono" && `${CLS_INPUT}--mono`, className),
1850
+ value,
1851
+ readOnly,
1852
+ "aria-label": label ?? rest["aria-label"],
1853
+ spellCheck: rest.spellCheck ?? variant !== "mono",
1854
+ onChange: (e) => onChange?.(e.target.value)
1855
+ }
1856
+ );
1857
+ }
1858
+ function Select({ options, value, onChange, mixed, disabled, label, className }) {
1859
+ usePanelPrimitives();
1860
+ return /* @__PURE__ */ jsxs2(
1861
+ "select",
1862
+ {
1863
+ className: cx(CLS_INPUT, `${CLS_INPUT}--select`, mixed && `${CLS_INPUT}--mixed`, className),
1864
+ value,
1865
+ disabled,
1866
+ "aria-label": label,
1867
+ onChange: (e) => onChange(e.target.value),
1868
+ children: [
1869
+ mixed || value === "" ? /* @__PURE__ */ jsx2("option", { value: "", children: mixed ? "\u2014" : "" }) : null,
1870
+ options.map((o) => /* @__PURE__ */ jsx2("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
1871
+ ]
1872
+ }
1873
+ );
1874
+ }
1875
+ function Swatch({ color, text, mixed, onClick, disabled, label, title, className }) {
1876
+ usePanelPrimitives();
1877
+ const empty = mixed || !color;
1878
+ return /* @__PURE__ */ jsxs2("button", { type: "button", className: cx(CLS_SWATCH, className), onClick, disabled, title, "aria-label": label, children: [
1879
+ /* @__PURE__ */ jsx2("span", { className: cx(`${CLS_SWATCH}-chip`, empty && `${CLS_SWATCH}-chip--empty`), style: empty ? void 0 : { background: color } }),
1880
+ /* @__PURE__ */ jsx2("span", { className: cx(`${CLS_SWATCH}-text`, mixed && `${CLS_INPUT}--mixed`), children: text ?? (color || "\u2014") })
1881
+ ] });
1882
+ }
1883
+ function AxisLabel({ children, scrubbable, onPointerDown, onPointerMove, onPointerUp, className }) {
1884
+ usePanelPrimitives();
1885
+ return /* @__PURE__ */ jsx2(
1886
+ "span",
1887
+ {
1888
+ className: cx(CLS_AXIS, scrubbable && `${CLS_AXIS}--scrub`, className),
1889
+ "aria-hidden": true,
1890
+ onPointerDown: scrubbable ? onPointerDown : void 0,
1891
+ onPointerMove: scrubbable ? onPointerMove : void 0,
1892
+ onPointerUp: scrubbable ? onPointerUp : void 0,
1893
+ children
1894
+ }
1895
+ );
1896
+ }
1707
1897
 
1708
1898
  // src/chrome/section.tsx
1709
1899
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -2030,6 +2220,11 @@ var COMPONENTS = {
2030
2220
  "chrome/Readout": Readout,
2031
2221
  "chrome/NumberInput": NumberInput,
2032
2222
  "chrome/Slider": Slider,
2223
+ "chrome/Input": Input,
2224
+ "chrome/TextArea": TextArea,
2225
+ "chrome/Select": Select,
2226
+ "chrome/Swatch": Swatch,
2227
+ "chrome/AxisLabel": AxisLabel,
2033
2228
  "chrome/SegmentedControl": SegmentedControl,
2034
2229
  "chrome/Toggle": Toggle,
2035
2230
  "chrome/FieldGroup": FieldGroup,
@@ -2086,6 +2281,21 @@ function gallery() {
2086
2281
  form("Slider", "quarter", () => /* @__PURE__ */ jsx7(Slider, { progress: 0.25, label: "Playhead", valueMax: 100, valueNow: 25, valueText: "25%" }));
2087
2282
  form("Slider", "end", () => /* @__PURE__ */ jsx7(Slider, { progress: 1, label: "Playhead" }));
2088
2283
  form("Slider", "disabled", () => /* @__PURE__ */ jsx7(Slider, { progress: 0.5, label: "Playhead", disabled: true }));
2284
+ form("Input", "value", () => /* @__PURE__ */ jsx7(Input, { value: "Hero image", onChange: noop, label: "Name" }));
2285
+ form("Input", "mixed", () => /* @__PURE__ */ jsx7(Input, { value: "", mixed: true, placeholder: "Mixed", onChange: noop, label: "Name" }));
2286
+ form("Input", "readonly", () => /* @__PURE__ */ jsx7(Input, { value: "node_7f3a", readOnly: true, variant: "mono", label: "Id" }));
2287
+ form("TextArea", "code", () => /* @__PURE__ */ jsx7(TextArea, { value: '{\n "kind": "frame"\n}', onChange: noop, variant: "mono", label: "Props" }));
2288
+ form("Select", "default", () => /* @__PURE__ */ jsx7(Select, { options: [{ value: "fill", label: "Fill" }, { value: "fit", label: "Fit" }, { value: "crop", label: "Crop" }], value: "fit", onChange: noop, label: "Scale mode" }));
2289
+ form("Select", "mixed", () => /* @__PURE__ */ jsx7(Select, { options: [{ value: "fill", label: "Fill" }, { value: "fit", label: "Fit" }], value: "", mixed: true, onChange: noop, label: "Scale mode" }));
2290
+ form("Swatch", "color", () => /* @__PURE__ */ jsx7(Swatch, { color: "steelblue", text: "steelblue", label: "Fill: steelblue", onClick: noop, title: "Edit in the Color panel" }));
2291
+ form("Swatch", "none", () => /* @__PURE__ */ jsx7(Swatch, { label: "Fill: none", onClick: noop }));
2292
+ form("Swatch", "mixed", () => /* @__PURE__ */ jsx7(Swatch, { mixed: true, text: "Mixed", label: "Fill: mixed", onClick: noop }));
2293
+ form("AxisLabel", "row", () => /* @__PURE__ */ jsxs7(Stack, { direction: "row", gap: 2, pad: "none", children: [
2294
+ /* @__PURE__ */ jsx7(AxisLabel, { scrubbable: true, children: "X" }),
2295
+ /* @__PURE__ */ jsx7(Input, { value: "120", onChange: noop, label: "X" }),
2296
+ /* @__PURE__ */ jsx7(AxisLabel, { scrubbable: true, children: "Y" }),
2297
+ /* @__PURE__ */ jsx7(Input, { value: "48", onChange: noop, label: "Y" })
2298
+ ] }));
2089
2299
  form("TextButton", "default", () => /* @__PURE__ */ jsx7(TextButton, { onClick: noop, children: "Clear all" }));
2090
2300
  form("TextButton", "disabled", () => /* @__PURE__ */ jsx7(TextButton, { onClick: noop, disabled: true, children: "Clear all" }));
2091
2301
  form("SearchField", "empty", () => /* @__PURE__ */ jsx7(SearchField, { value: "", onChange: noop, placeholder: "Filter rows\u2026", label: "Filter" }));
@@ -2173,6 +2383,7 @@ function gallery() {
2173
2383
  return out;
2174
2384
  }
2175
2385
  export {
2386
+ AxisLabel,
2176
2387
  Badge,
2177
2388
  CLS_BADGE,
2178
2389
  CLS_BAR,
@@ -2239,6 +2450,7 @@ export {
2239
2450
  FieldGroup,
2240
2451
  IconButton,
2241
2452
  IconFrame,
2453
+ Input,
2242
2454
  LoadingState,
2243
2455
  NumberInput,
2244
2456
  PANEL_COLORS,
@@ -2257,6 +2469,7 @@ export {
2257
2469
  SearchGlyph,
2258
2470
  Section,
2259
2471
  SegmentedControl,
2472
+ Select,
2260
2473
  Slider,
2261
2474
  Sparkline,
2262
2475
  Stack,
@@ -2264,7 +2477,9 @@ export {
2264
2477
  StatusBadge,
2265
2478
  StatusBar,
2266
2479
  StatusDot,
2480
+ Swatch,
2267
2481
  Text,
2482
+ TextArea,
2268
2483
  TextButton,
2269
2484
  Toggle,
2270
2485
  Toolbar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenosystem/components",
3
- "version": "0.10.2",
3
+ "version": "0.11.1",
4
4
  "description": "XENO Components \u2014 rung 2 of the front ladder: molecules composed from @xenosystem/elements, with no host contract. Families as subpaths: /auth, /navigation, /feedback, /data.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",