webadwaita 0.2.0 → 0.2.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.
Files changed (52) hide show
  1. package/README.md +25 -32
  2. package/dist/native/Box/index.js +120 -0
  3. package/{src/Button/index.tsx → dist/native/Button/index.js} +32 -50
  4. package/{src/Card/index.tsx → dist/native/Card/index.js} +25 -25
  5. package/{src/Checkbox/index.tsx → dist/native/Checkbox/index.js} +41 -48
  6. package/{src/Entry/index.tsx → dist/native/Entry/index.js} +24 -42
  7. package/{src/HeaderBar/index.tsx → dist/native/HeaderBar/index.js} +31 -30
  8. package/{src/Switch/index.tsx → dist/native/Switch/index.js} +31 -37
  9. package/dist/native/index.js +8 -0
  10. package/{src/theme.ts → dist/native/theme.js} +4 -7
  11. package/{src/tokens.css.ts → dist/native/tokens.css.js} +3 -17
  12. package/dist/types/Box/index.d.ts +23 -0
  13. package/dist/types/Box/index.d.ts.map +1 -0
  14. package/dist/types/Button/index.d.ts +22 -0
  15. package/dist/types/Button/index.d.ts.map +1 -0
  16. package/dist/types/Card/index.d.ts +14 -0
  17. package/dist/types/Card/index.d.ts.map +1 -0
  18. package/dist/types/Checkbox/index.d.ts +13 -0
  19. package/dist/types/Checkbox/index.d.ts.map +1 -0
  20. package/dist/types/Entry/index.d.ts +17 -0
  21. package/dist/types/Entry/index.d.ts.map +1 -0
  22. package/dist/types/HeaderBar/index.d.ts +16 -0
  23. package/dist/types/HeaderBar/index.d.ts.map +1 -0
  24. package/dist/types/Switch/index.d.ts +13 -0
  25. package/dist/types/Switch/index.d.ts.map +1 -0
  26. package/{src/index.ts → dist/types/index.d.ts} +1 -7
  27. package/dist/types/index.d.ts.map +1 -0
  28. package/dist/types/theme.d.ts +77 -0
  29. package/dist/types/theme.d.ts.map +1 -0
  30. package/dist/types/tokens.css.d.ts +76 -0
  31. package/dist/types/tokens.css.d.ts.map +1 -0
  32. package/dist/web/Box/index.js +150 -0
  33. package/dist/web/Box/styles.css +39 -0
  34. package/dist/web/Button/index.js +132 -0
  35. package/dist/web/Button/styles.css +81 -0
  36. package/dist/web/Card/index.js +75 -0
  37. package/dist/web/Card/styles.css +91 -0
  38. package/dist/web/Checkbox/index.js +116 -0
  39. package/dist/web/Checkbox/styles.css +99 -0
  40. package/dist/web/Entry/index.js +74 -0
  41. package/dist/web/Entry/styles.css +113 -0
  42. package/dist/web/HeaderBar/index.js +87 -0
  43. package/dist/web/HeaderBar/styles.css +117 -0
  44. package/dist/web/Switch/index.js +86 -0
  45. package/dist/web/Switch/styles.css +134 -0
  46. package/dist/web/_tokens/styles.css +32 -0
  47. package/dist/web/index.js +8 -0
  48. package/dist/web/theme.js +16 -0
  49. package/dist/web/tokens.css.js +79 -0
  50. package/package.json +28 -9
  51. package/babel.config.cjs +0 -23
  52. package/src/Box/index.tsx +0 -105
package/README.md CHANGED
@@ -18,10 +18,6 @@ react-strict-dom is Meta's strict subset of `<html.*>` primitives plus a `css.cr
18
18
  | `Card` | Surface with optional title; `boxed` and `flat` variants. |
19
19
  | `HeaderBar` | Top bar with leading / centered title / trailing slots. |
20
20
 
21
- All design tokens live in [`src/tokens.css.ts`](./src/tokens.css.ts) — palette, radius, spacing, typography, motion. The `.css.ts` filename is required: react-strict-dom's Babel preset configures StyleX to treat `.css` as the token-file extension, so any file holding `css.defineConsts` / `css.defineVars` calls must be named `<name>.css.ts`.
22
-
23
- [`src/theme.ts`](./src/theme.ts) re-exports the same tokens for runtime use (e.g. computed inline styles in app code).
24
-
25
21
  ## Install
26
22
 
27
23
  ```sh
@@ -32,37 +28,37 @@ Peer deps: `react ^19`, plus `react-dom` (web) or `react-native >= 0.79` (native
32
28
 
33
29
  ## Setup
34
30
 
35
- **WebAdwaita ships TypeScript source not a pre-built bundle.** This is the same pattern Meta's `apps/example-ui` uses, and it's deliberate: the StyleX class names baked into a pre-built bundle would not match the CSS rules extracted by the consumer's PostCSS pipeline. Shipping source lets a single Babel pass at consumer build time produce both the class names *and* the matching CSS.
31
+ WebAdwaita ships precompiled JS plus per-component CSS no special bundler config required. Install, import, render:
36
32
 
37
- That means your app's bundler must run the `react-strict-dom` Babel preset over `node_modules/webadwaita/src` and your PostCSS config must scan the same files for CSS extraction.
38
-
39
- **Vite (web):** see [playground/vite.config.ts](./playground/vite.config.ts) and [playground/postcss.config.cjs](./playground/postcss.config.cjs) for the working setup. The key bits:
33
+ ```sh
34
+ npm install webadwaita react-strict-dom
35
+ ```
40
36
 
41
- ```ts
42
- // vite.config.ts
43
- plugins: [
44
- react({
45
- babel: { configFile: true },
46
- include: [/\.[jt]sx?$/, '<...>/node_modules/webadwaita/src/**']
47
- }),
48
- babel({ filter: /\.[jt]sx?$/ })
49
- ]
37
+ ```tsx
38
+ import { Button, Card } from 'webadwaita';
50
39
  ```
51
40
 
52
- ```js
53
- // postcss.config.cjs
54
- 'react-strict-dom/postcss-plugin': {
55
- include: [
56
- 'src/**/*.{ts,tsx}',
57
- 'node_modules/webadwaita/src/**/*.{ts,tsx}'
58
- ],
59
- babelConfig
60
- }
41
+ That's it. Each component side-effect-imports its own CSS chunk, so your bundler ships only the styles for the components you actually use (works in Next.js, Vite, CRA, anywhere with a modern bundler honoring `package.json#sideEffects`).
42
+
43
+ **Native (Expo / Metro):** the package's `react-native` exports condition routes Metro to a precompiled native build that uses RN `StyleSheet` at runtime. No Babel preset to wire up in your app — just `import { Button } from 'webadwaita'`.
44
+
45
+ ### How the build works
46
+
47
+ The library has a build step (`npm run build`) that produces:
48
+
49
+ ```
50
+ dist/
51
+ web/<Component>/index.js ← starts with `import './styles.css'`
52
+ web/<Component>/styles.css ← only that component's atomic rules
53
+ web/_tokens/styles.css ← shared baseline (loaded once)
54
+ native/<Component>/index.js ← RN StyleSheet output, no CSS
55
+ types/<Component>/index.d.ts
61
56
  ```
62
57
 
63
- Then add a `strict.css` file with the directive `@react-strict-dom;` and import it from your app's entry point — PostCSS replaces the directive with the generated CSS.
58
+ Two design-token notes carry over to consumer code:
64
59
 
65
- **Expo / Metro (native):** Metro picks up the source automatically. Add the `react-strict-dom/babel-preset` to your `babel.config.js` exactly like the [react-strict-dom expo-app example](https://github.com/facebook/react-strict-dom/blob/main/apps/expo-app/babel.config.js).
60
+ - Tokens still live in [`src/tokens.css.ts`](./src/tokens.css.ts). The `.css.ts` filename is required: react-strict-dom's StyleX config treats `.css` as the token-file extension, so any file calling `css.defineConsts` / `css.defineVars` must follow that convention.
61
+ - [`src/theme.ts`](./src/theme.ts) re-exports the same tokens for runtime use.
66
62
 
67
63
  ## Use
68
64
 
@@ -90,13 +86,10 @@ export function Settings() {
90
86
 
91
87
  ## Develop
92
88
 
93
- There is no library build step — consumers transform the TypeScript source directly. `npm run typecheck` is the only "build-like" command for the lib itself.
94
-
95
- Two demo apps live under `apps/`:
96
-
97
89
  ```sh
98
90
  # library
99
91
  npm install
92
+ npm run build # produces dist/{web,native,types}
100
93
  npm run typecheck
101
94
 
102
95
  # web Storybook (Vite-powered)
@@ -0,0 +1,120 @@
1
+ import * as React from 'react';
2
+ import { css, html } from 'react-strict-dom';
3
+ import { spacing } from '../tokens.css';
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ /**
6
+ * Layout primitive — a flex container, the building block for everything else.
7
+ * Adwaita-inspired apps lean heavily on simple horizontal/vertical stacks.
8
+ */
9
+ export function Box(props) {
10
+ const {
11
+ children,
12
+ direction = 'column',
13
+ gap,
14
+ padding,
15
+ align,
16
+ justify,
17
+ grow,
18
+ wrap,
19
+ style
20
+ } = props;
21
+ return /*#__PURE__*/_jsx(html.div, {
22
+ style: [styles.base, direction === 'row' ? styles.row : styles.column, wrap && styles.wrap, grow && styles.grow, align != null && alignStyles[align], justify != null && justifyStyles[justify], gap != null && gapStyles[gap], padding != null && paddingStyles[padding], style],
23
+ children: children
24
+ });
25
+ }
26
+ const styles = css.create({
27
+ base: {
28
+ display: 'flex',
29
+ minWidth: 0
30
+ },
31
+ row: {
32
+ flexDirection: 'row'
33
+ },
34
+ column: {
35
+ flexDirection: 'column'
36
+ },
37
+ wrap: {
38
+ flexWrap: 'wrap'
39
+ },
40
+ grow: {
41
+ flexGrow: 1
42
+ }
43
+ });
44
+ const alignStyles = css.create({
45
+ 'flex-start': {
46
+ alignItems: 'flex-start'
47
+ },
48
+ center: {
49
+ alignItems: 'center'
50
+ },
51
+ 'flex-end': {
52
+ alignItems: 'flex-end'
53
+ },
54
+ stretch: {
55
+ alignItems: 'stretch'
56
+ },
57
+ baseline: {
58
+ alignItems: 'baseline'
59
+ }
60
+ });
61
+ const justifyStyles = css.create({
62
+ 'flex-start': {
63
+ justifyContent: 'flex-start'
64
+ },
65
+ center: {
66
+ justifyContent: 'center'
67
+ },
68
+ 'flex-end': {
69
+ justifyContent: 'flex-end'
70
+ },
71
+ 'space-between': {
72
+ justifyContent: 'space-between'
73
+ },
74
+ 'space-around': {
75
+ justifyContent: 'space-around'
76
+ },
77
+ 'space-evenly': {
78
+ justifyContent: 'space-evenly'
79
+ }
80
+ });
81
+ const gapStyles = css.create({
82
+ xs: {
83
+ gap: spacing.xs
84
+ },
85
+ sm: {
86
+ gap: spacing.sm
87
+ },
88
+ md: {
89
+ gap: spacing.md
90
+ },
91
+ lg: {
92
+ gap: spacing.lg
93
+ },
94
+ xl: {
95
+ gap: spacing.xl
96
+ },
97
+ xxl: {
98
+ gap: spacing.xxl
99
+ }
100
+ });
101
+ const paddingStyles = css.create({
102
+ xs: {
103
+ padding: spacing.xs
104
+ },
105
+ sm: {
106
+ padding: spacing.sm
107
+ },
108
+ md: {
109
+ padding: spacing.md
110
+ },
111
+ lg: {
112
+ padding: spacing.lg
113
+ },
114
+ xl: {
115
+ padding: spacing.xl
116
+ },
117
+ xxl: {
118
+ padding: spacing.xxl
119
+ }
120
+ });
@@ -1,26 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { css, html } from 'react-strict-dom';
3
3
  import { palette, radius, spacing, typography, motion } from '../tokens.css';
4
-
5
- export type ButtonVariant =
6
- | 'default'
7
- | 'suggested'
8
- | 'destructive'
9
- | 'flat'
10
- | 'pill';
11
-
12
- export type ButtonSize = 'sm' | 'md' | 'lg';
13
-
14
- export type ButtonProps = {
15
- children?: React.ReactNode;
16
- variant?: ButtonVariant;
17
- size?: ButtonSize;
18
- disabled?: boolean;
19
- fullWidth?: boolean;
20
- onPress?: () => void;
21
- type?: 'button' | 'submit';
22
- };
23
-
4
+ import { jsx as _jsx } from "react/jsx-runtime";
24
5
  /**
25
6
  * Adwaita-style button. Variants follow libadwaita conventions:
26
7
  * - default: flat-ish neutral surface
@@ -29,7 +10,7 @@ export type ButtonProps = {
29
10
  * - flat: no background, only on hover
30
11
  * - pill: fully rounded (Adwaita "circular" buttons)
31
12
  */
32
- export function Button(props: ButtonProps) {
13
+ export function Button(props) {
33
14
  const {
34
15
  children,
35
16
  variant = 'default',
@@ -39,27 +20,17 @@ export function Button(props: ButtonProps) {
39
20
  onPress,
40
21
  type = 'button'
41
22
  } = props;
42
-
43
- return (
44
- <html.button
45
- type={type}
46
- disabled={disabled}
47
- onClick={onPress}
48
- style={[
49
- styles.base,
50
- sizeStyles[size],
51
- variantStyles[variant],
52
- fullWidth && styles.fullWidth,
53
- disabled && styles.disabled
54
- ]}
55
- >
56
- <html.span style={[styles.label, labelSizeStyles[size]]}>
57
- {children}
58
- </html.span>
59
- </html.button>
60
- );
23
+ return /*#__PURE__*/_jsx(html.button, {
24
+ type: type,
25
+ disabled: disabled,
26
+ onClick: onPress,
27
+ style: [styles.base, sizeStyles[size], variantStyles[variant], fullWidth && styles.fullWidth, disabled && styles.disabled],
28
+ children: /*#__PURE__*/_jsx(html.span, {
29
+ style: [styles.label, labelSizeStyles[size]],
30
+ children: children
31
+ })
32
+ });
61
33
  }
62
-
63
34
  const styles = css.create({
64
35
  base: {
65
36
  display: 'flex',
@@ -79,10 +50,14 @@ const styles = css.create({
79
50
  fontWeight: typography.weightStrong,
80
51
  textAlign: 'center'
81
52
  },
82
- fullWidth: { alignSelf: 'stretch' },
83
- disabled: { opacity: 0.5, cursor: 'not-allowed' }
53
+ fullWidth: {
54
+ alignSelf: 'stretch'
55
+ },
56
+ disabled: {
57
+ opacity: 0.5,
58
+ cursor: 'not-allowed'
59
+ }
84
60
  });
85
-
86
61
  const sizeStyles = css.create({
87
62
  sm: {
88
63
  paddingInline: spacing.md,
@@ -100,13 +75,20 @@ const sizeStyles = css.create({
100
75
  height: 44
101
76
  }
102
77
  });
103
-
104
78
  const labelSizeStyles = css.create({
105
- sm: { fontSize: typography.captionSize, lineHeight: typography.captionLineHeight },
106
- md: { fontSize: typography.bodySize, lineHeight: typography.bodyLineHeight },
107
- lg: { fontSize: typography.title4Size, lineHeight: typography.title4LineHeight }
79
+ sm: {
80
+ fontSize: typography.captionSize,
81
+ lineHeight: typography.captionLineHeight
82
+ },
83
+ md: {
84
+ fontSize: typography.bodySize,
85
+ lineHeight: typography.bodyLineHeight
86
+ },
87
+ lg: {
88
+ fontSize: typography.title4Size,
89
+ lineHeight: typography.title4LineHeight
90
+ }
108
91
  });
109
-
110
92
  const variantStyles = css.create({
111
93
  default: {
112
94
  backgroundColor: {
@@ -150,4 +132,4 @@ const variantStyles = css.create({
150
132
  borderRadius: radius.pill,
151
133
  paddingInline: spacing.xl
152
134
  }
153
- });
135
+ });
@@ -1,34 +1,32 @@
1
1
  import * as React from 'react';
2
2
  import { css, html } from 'react-strict-dom';
3
3
  import { palette, radius, spacing, typography } from '../tokens.css';
4
-
5
- export type CardProps = {
6
- children?: React.ReactNode;
7
- title?: React.ReactNode;
8
- /** "boxed" matches AdwBoxedList — solid surface w/ visible border. */
9
- variant?: 'boxed' | 'flat';
10
- padding?: boolean;
11
- };
12
-
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
5
  /**
14
6
  * Adwaita-style card surface. Default has a soft shadow + rounded corners;
15
7
  * "flat" drops the shadow for use inside other surfaces.
16
8
  */
17
- export function Card(props: CardProps) {
18
- const { children, title, variant = 'boxed', padding = true } = props;
19
-
20
- return (
21
- <html.div style={[styles.card, variant === 'boxed' ? styles.boxed : styles.flat]}>
22
- {title != null && (
23
- <html.div style={styles.header}>
24
- <html.h3 style={styles.titleText}>{title}</html.h3>
25
- </html.div>
26
- )}
27
- <html.div style={padding ? styles.body : null}>{children}</html.div>
28
- </html.div>
29
- );
9
+ export function Card(props) {
10
+ const {
11
+ children,
12
+ title,
13
+ variant = 'boxed',
14
+ padding = true
15
+ } = props;
16
+ return /*#__PURE__*/_jsxs(html.div, {
17
+ style: [styles.card, variant === 'boxed' ? styles.boxed : styles.flat],
18
+ children: [title != null && /*#__PURE__*/_jsx(html.div, {
19
+ style: styles.header,
20
+ children: /*#__PURE__*/_jsx(html.h3, {
21
+ style: styles.titleText,
22
+ children: title
23
+ })
24
+ }), /*#__PURE__*/_jsx(html.div, {
25
+ style: padding ? styles.body : null,
26
+ children: children
27
+ })]
28
+ });
30
29
  }
31
-
32
30
  const styles = css.create({
33
31
  card: {
34
32
  backgroundColor: palette.cardBg,
@@ -63,5 +61,7 @@ const styles = css.create({
63
61
  fontWeight: typography.weightBold,
64
62
  color: palette.fg
65
63
  },
66
- body: { padding: spacing.lg }
67
- });
64
+ body: {
65
+ padding: spacing.lg
66
+ }
67
+ });
@@ -1,63 +1,53 @@
1
1
  import * as React from 'react';
2
2
  import { css, html } from 'react-strict-dom';
3
3
  import { palette, radius, motion, typography, spacing } from '../tokens.css';
4
-
5
- export type CheckboxProps = {
6
- checked: boolean;
7
- disabled?: boolean;
8
- onChange?: (checked: boolean) => void;
9
- label?: React.ReactNode;
10
- };
11
-
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
5
  /**
13
6
  * Adwaita-style checkbox. Solid blue square with a unicode check when on,
14
7
  * subtle outlined square when off.
15
8
  */
16
- export function Checkbox(props: CheckboxProps) {
17
- const { checked, disabled = false, onChange, label } = props;
18
-
9
+ export function Checkbox(props) {
10
+ const {
11
+ checked,
12
+ disabled = false,
13
+ onChange,
14
+ label
15
+ } = props;
19
16
  const toggle = () => {
20
17
  if (!disabled) onChange?.(!checked);
21
18
  };
22
-
23
- const box = (
24
- <html.span style={[styles.box, checked ? styles.boxOn : styles.boxOff]}>
25
- {checked && <html.span style={styles.check}>{'✓'}</html.span>}
26
- </html.span>
27
- );
28
-
19
+ const box = /*#__PURE__*/_jsx(html.span, {
20
+ style: [styles.box, checked ? styles.boxOn : styles.boxOff],
21
+ children: checked && /*#__PURE__*/_jsx(html.span, {
22
+ style: styles.check,
23
+ children: '✓'
24
+ })
25
+ });
29
26
  if (label == null) {
30
- return (
31
- <html.button
32
- type="button"
33
- role="checkbox"
34
- aria-checked={checked}
35
- disabled={disabled}
36
- onClick={toggle}
37
- style={[styles.bareButton, disabled && styles.disabled]}
38
- >
39
- {box}
40
- </html.button>
41
- );
27
+ return /*#__PURE__*/_jsx(html.button, {
28
+ type: "button",
29
+ role: "checkbox",
30
+ "aria-checked": checked,
31
+ disabled: disabled,
32
+ onClick: toggle,
33
+ style: [styles.bareButton, disabled && styles.disabled],
34
+ children: box
35
+ });
42
36
  }
43
-
44
- return (
45
- <html.button
46
- type="button"
47
- role="checkbox"
48
- aria-checked={checked}
49
- disabled={disabled}
50
- onClick={toggle}
51
- style={[styles.row, disabled && styles.disabled]}
52
- >
53
- {box}
54
- <html.span style={styles.label}>{label}</html.span>
55
- </html.button>
56
- );
37
+ return /*#__PURE__*/_jsxs(html.button, {
38
+ type: "button",
39
+ role: "checkbox",
40
+ "aria-checked": checked,
41
+ disabled: disabled,
42
+ onClick: toggle,
43
+ style: [styles.row, disabled && styles.disabled],
44
+ children: [box, /*#__PURE__*/_jsx(html.span, {
45
+ style: styles.label,
46
+ children: label
47
+ })]
48
+ });
57
49
  }
58
-
59
50
  const SIZE = 18;
60
-
61
51
  const styles = css.create({
62
52
  bareButton: {
63
53
  backgroundColor: 'transparent',
@@ -115,5 +105,8 @@ const styles = css.create({
115
105
  fontSize: typography.bodySize,
116
106
  lineHeight: typography.bodyLineHeight
117
107
  },
118
- disabled: { opacity: 0.5, cursor: 'not-allowed' }
119
- });
108
+ disabled: {
109
+ opacity: 0.5,
110
+ cursor: 'not-allowed'
111
+ }
112
+ });
@@ -1,24 +1,12 @@
1
1
  import * as React from 'react';
2
2
  import { css, html } from 'react-strict-dom';
3
3
  import { palette, radius, spacing, typography, motion } from '../tokens.css';
4
-
5
- export type EntryProps = {
6
- value?: string;
7
- defaultValue?: string;
8
- placeholder?: string;
9
- disabled?: boolean;
10
- readOnly?: boolean;
11
- invalid?: boolean;
12
- type?: 'text' | 'password' | 'email' | 'search' | 'tel' | 'url' | 'number';
13
- onChange?: (value: string) => void;
14
- onSubmit?: (value: string) => void;
15
- };
16
-
4
+ import { jsx as _jsx } from "react/jsx-runtime";
17
5
  /**
18
6
  * Adwaita-style text entry. Underline focus indicator + subtle filled surface,
19
7
  * like libadwaita's `AdwEntry`.
20
8
  */
21
- export function Entry(props: EntryProps) {
9
+ export function Entry(props) {
22
10
  const {
23
11
  value,
24
12
  defaultValue,
@@ -30,33 +18,24 @@ export function Entry(props: EntryProps) {
30
18
  onChange,
31
19
  onSubmit
32
20
  } = props;
33
-
34
- const latestValueRef = React.useRef<string>(value ?? defaultValue ?? '');
35
-
36
- return (
37
- <html.input
38
- type={type}
39
- value={value}
40
- defaultValue={defaultValue}
41
- placeholder={placeholder}
42
- disabled={disabled}
43
- readOnly={readOnly}
44
- onChange={(e: { target: { value: string } }) => {
45
- latestValueRef.current = e.target.value;
46
- onChange?.(e.target.value);
47
- }}
48
- onKeyDown={(e: { key: string }) => {
49
- if (e.key === 'Enter') onSubmit?.(latestValueRef.current);
50
- }}
51
- style={[
52
- styles.input,
53
- invalid && styles.invalid,
54
- disabled && styles.disabled
55
- ]}
56
- />
57
- );
21
+ const latestValueRef = React.useRef(value ?? defaultValue ?? '');
22
+ return /*#__PURE__*/_jsx(html.input, {
23
+ type: type,
24
+ value: value,
25
+ defaultValue: defaultValue,
26
+ placeholder: placeholder,
27
+ disabled: disabled,
28
+ readOnly: readOnly,
29
+ onChange: e => {
30
+ latestValueRef.current = e.target.value;
31
+ onChange?.(e.target.value);
32
+ },
33
+ onKeyDown: e => {
34
+ if (e.key === 'Enter') onSubmit?.(latestValueRef.current);
35
+ },
36
+ style: [styles.input, invalid && styles.invalid, disabled && styles.disabled]
37
+ });
58
38
  }
59
-
60
39
  const styles = css.create({
61
40
  input: {
62
41
  backgroundColor: palette.subtleBg,
@@ -90,5 +69,8 @@ const styles = css.create({
90
69
  ':focus': palette.destructiveBg
91
70
  }
92
71
  },
93
- disabled: { opacity: 0.5, cursor: 'not-allowed' }
94
- });
72
+ disabled: {
73
+ opacity: 0.5,
74
+ cursor: 'not-allowed'
75
+ }
76
+ });
@@ -1,40 +1,39 @@
1
1
  import * as React from 'react';
2
2
  import { css, html } from 'react-strict-dom';
3
3
  import { palette, spacing, typography } from '../tokens.css';
4
-
5
- export type HeaderBarProps = {
6
- title?: React.ReactNode;
7
- subtitle?: React.ReactNode;
8
- /** Buttons / actions to place at the start (left) of the bar. */
9
- start?: React.ReactNode;
10
- /** Buttons / actions to place at the end (right) of the bar. */
11
- end?: React.ReactNode;
12
- };
13
-
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
5
  /**
15
6
  * Top-level header bar matching libadwaita's AdwHeaderBar — a thin titled
16
7
  * strip that sits at the top of a window. Centered title with optional
17
8
  * leading/trailing slots.
18
9
  */
19
- export function HeaderBar(props: HeaderBarProps) {
20
- const { title, subtitle, start, end } = props;
21
-
22
- return (
23
- <html.header style={styles.bar}>
24
- <html.div style={styles.side}>{start}</html.div>
25
- <html.div style={styles.titleBlock}>
26
- {title != null && (
27
- <html.span style={styles.title}>{title}</html.span>
28
- )}
29
- {subtitle != null && (
30
- <html.span style={styles.subtitle}>{subtitle}</html.span>
31
- )}
32
- </html.div>
33
- <html.div style={[styles.side, styles.sideEnd]}>{end}</html.div>
34
- </html.header>
35
- );
10
+ export function HeaderBar(props) {
11
+ const {
12
+ title,
13
+ subtitle,
14
+ start,
15
+ end
16
+ } = props;
17
+ return /*#__PURE__*/_jsxs(html.header, {
18
+ style: styles.bar,
19
+ children: [/*#__PURE__*/_jsx(html.div, {
20
+ style: styles.side,
21
+ children: start
22
+ }), /*#__PURE__*/_jsxs(html.div, {
23
+ style: styles.titleBlock,
24
+ children: [title != null && /*#__PURE__*/_jsx(html.span, {
25
+ style: styles.title,
26
+ children: title
27
+ }), subtitle != null && /*#__PURE__*/_jsx(html.span, {
28
+ style: styles.subtitle,
29
+ children: subtitle
30
+ })]
31
+ }), /*#__PURE__*/_jsx(html.div, {
32
+ style: [styles.side, styles.sideEnd],
33
+ children: end
34
+ })]
35
+ });
36
36
  }
37
-
38
37
  const styles = css.create({
39
38
  bar: {
40
39
  display: 'flex',
@@ -57,7 +56,9 @@ const styles = css.create({
57
56
  flexBasis: 0,
58
57
  flexGrow: 1
59
58
  },
60
- sideEnd: { justifyContent: 'flex-end' },
59
+ sideEnd: {
60
+ justifyContent: 'flex-end'
61
+ },
61
62
  titleBlock: {
62
63
  display: 'flex',
63
64
  flexDirection: 'column',
@@ -75,4 +76,4 @@ const styles = css.create({
75
76
  fontSize: typography.captionSize,
76
77
  color: palette.fgMuted
77
78
  }
78
- });
79
+ });