elbe-ui 0.3.1 → 0.4.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 (80) hide show
  1. package/README.md +12 -0
  2. package/dist/elbe.css +57 -56
  3. package/dist/elbe.css.map +1 -1
  4. package/dist/index.d.ts +6 -3
  5. package/dist/index.js +6 -3
  6. package/dist/ui/components/badge.d.ts +1 -1
  7. package/dist/ui/components/banner.d.ts +3 -2
  8. package/dist/ui/components/banner.js +13 -7
  9. package/dist/ui/components/base/box.d.ts +28 -27
  10. package/dist/ui/components/base/card.d.ts +6 -4
  11. package/dist/ui/components/base/card.js +6 -2
  12. package/dist/ui/components/base/roles.d.ts +1 -0
  13. package/dist/ui/components/base/roles.js +1 -0
  14. package/dist/ui/components/button/button.d.ts +1 -0
  15. package/dist/ui/components/button/button.js +12 -4
  16. package/dist/ui/components/button/choose_button.js +3 -2
  17. package/dist/ui/components/button/icon_button.d.ts +3 -0
  18. package/dist/ui/components/button/icon_button.js +9 -2
  19. package/dist/ui/components/button/toggle_button.js +7 -1
  20. package/dist/ui/components/dialog.d.ts +1 -1
  21. package/dist/ui/components/footer.js +1 -1
  22. package/dist/ui/components/input/checkbox.d.ts +2 -5
  23. package/dist/ui/components/input/checkbox.js +10 -19
  24. package/dist/ui/components/input/range.js +4 -1
  25. package/dist/ui/components/input/select.js +1 -1
  26. package/dist/ui/components/input/switch.d.ts +6 -2
  27. package/dist/ui/components/input/switch.js +25 -22
  28. package/dist/ui/components/input/text/input_field.d.ts +28 -0
  29. package/dist/ui/components/input/text/input_field.js +70 -0
  30. package/dist/ui/components/input/text/multi_line.d.ts +10 -0
  31. package/dist/ui/components/input/text/multi_line.js +19 -0
  32. package/dist/ui/components/input/text/single_line.d.ts +18 -0
  33. package/dist/ui/components/input/text/single_line.js +28 -0
  34. package/dist/ui/components/layout/app_base.d.ts +14 -0
  35. package/dist/ui/components/layout/app_base.js +30 -0
  36. package/dist/ui/components/layout/ctx_app_base.d.ts +16 -0
  37. package/dist/ui/components/layout/ctx_app_base.js +13 -0
  38. package/dist/ui/components/layout/flex.d.ts +4 -2
  39. package/dist/ui/components/layout/flex.js +13 -5
  40. package/dist/ui/components/layout/header.d.ts +18 -18
  41. package/dist/ui/components/layout/header.js +54 -48
  42. package/dist/ui/components/layout/menu.d.ts +12 -0
  43. package/dist/ui/components/layout/menu.js +59 -0
  44. package/dist/ui/components/layout/spaced.d.ts +4 -2
  45. package/dist/ui/components/layout/spaced.js +6 -2
  46. package/dist/ui/components/layout/toolbar.d.ts +7 -0
  47. package/dist/ui/components/layout/toolbar.js +71 -0
  48. package/dist/ui/components/progress_bar.js +7 -5
  49. package/dist/ui/components/spinner.js +4 -0
  50. package/dist/ui/components/text.d.ts +1 -1
  51. package/dist/ui/components/text.js +4 -4
  52. package/dist/ui/theme/color_theme.d.ts +1 -1
  53. package/dist/ui/theme/color_theme.js +63 -52
  54. package/dist/ui/theme/colors.d.ts +33 -13
  55. package/dist/ui/theme/colors.js +42 -15
  56. package/dist/ui/theme/geometry_theme.js +2 -4
  57. package/dist/ui/theme/seed.d.ts +18 -8
  58. package/dist/ui/theme/seed.js +4 -1
  59. package/dist/ui/theme/theme.d.ts +3 -5
  60. package/dist/ui/theme/theme.js +9 -12
  61. package/dist/ui/theme/theme_context.d.ts +18 -0
  62. package/dist/ui/theme/theme_context.js +26 -0
  63. package/dist/ui/theme/type_theme.js +5 -2
  64. package/dist/ui/util/ctx_toolbar.d.ts +8 -0
  65. package/dist/ui/util/ctx_toolbar.js +9 -0
  66. package/dist/ui/util/l10n/l10n.d.ts +35 -0
  67. package/dist/ui/util/l10n/l10n.js +68 -0
  68. package/dist/ui/util/single_key.d.ts +11 -0
  69. package/dist/ui/util/single_key.js +1 -0
  70. package/dist/ui/util/types.d.ts +9 -0
  71. package/dist/ui/util/types.js +1 -0
  72. package/dist/ui/util/util.d.ts +5 -3
  73. package/dist/ui/util/util.js +36 -1
  74. package/package.json +1 -1
  75. package/dist/ui/components/input/input_field.d.ts +0 -22
  76. package/dist/ui/components/input/input_field.js +0 -36
  77. package/dist/ui/components/input/text_area.d.ts +0 -10
  78. package/dist/ui/components/input/text_area.js +0 -22
  79. package/dist/ui/components/layout/scaffold.d.ts +0 -15
  80. package/dist/ui/components/layout/scaffold.js +0 -31
@@ -1,18 +1,30 @@
1
- import { ColorSeedColors, ColorThemeSeed, PartialColorThemeSeed } from "./seed";
1
+ import { ColorThemeSeed, PartialColorThemeSeed, ThemeColors } from "./seed";
2
+ export type ElbeColorContrasts = "normal" | "highvis";
2
3
  export type ElbeColorModes = "light" | "dark";
3
4
  export type ElbeColorSchemes = "primary" | "secondary" | "inverse";
4
5
  export type ElbeAlertKinds = "success" | "warning" | "error" | "info";
5
6
  export type ElbeColorKinds = ElbeAlertKinds | "plain" | "accent";
6
7
  export type ElbeColorManners = "major" | "minor" | "flat" | "plain";
8
+ export declare const cContrasts: ElbeColorContrasts[];
7
9
  export declare const cModes: ElbeColorModes[];
8
10
  export declare const cSchemes: ElbeColorSchemes[];
9
11
  export declare const cKinds: ElbeColorKinds[];
10
12
  export declare const cManners: ElbeColorManners[];
11
13
  export declare const cStates: string[];
12
14
  export declare const cLayers: string[];
13
- export type SeedSelector = (seed: ColorThemeSeed, base: LayerColor) => LayerColor;
14
- export type SeedFlatSelector = (seed: ColorThemeSeed, base: LayerColor, style?: LayerColor) => LayerColor;
15
- export type SeedStyleSelector = (seed: ColorThemeSeed, base: LayerColor, style: LayerColor) => LayerColor;
15
+ type _SeedSelInput = {
16
+ path: string[];
17
+ seed: ColorThemeSeed;
18
+ base: LayerColor;
19
+ };
20
+ export type SeedSelector = (p: _SeedSelInput) => LayerColor;
21
+ export type SeedModifier = (p: _SeedSelInput) => ColorThemeSeed;
22
+ export type SeedFlatSelector = (p: _SeedSelInput & {
23
+ style?: LayerColor;
24
+ }) => LayerColor;
25
+ export type SeedStyleSelector = (p: _SeedSelInput & {
26
+ style: LayerColor;
27
+ }) => LayerColor;
16
28
  export declare class RGBAColor {
17
29
  r: number;
18
30
  g: number;
@@ -67,7 +79,7 @@ export declare class StateColor extends LayerColor {
67
79
  disabled: LayerColor;
68
80
  constructor(neutral: LayerColor, hover: LayerColor, active: LayerColor, disabled: LayerColor);
69
81
  asCss(): string;
70
- static generate(_: ColorThemeSeed, context: LayerColor, style: LayerColor, fromFront?: boolean): StateColor;
82
+ static generate(p: string[], _: ColorThemeSeed, context: LayerColor, style: LayerColor, fromFront?: boolean): StateColor;
71
83
  }
72
84
  export declare class MannerColor extends StateColor {
73
85
  major: StateColor | null;
@@ -76,7 +88,7 @@ export declare class MannerColor extends StateColor {
76
88
  constructor(major: StateColor | null, minor: StateColor | null, flat: StateColor);
77
89
  asCss(fallback?: MannerColor): string;
78
90
  raisedCss(): string;
79
- static generate(s: ColorThemeSeed, c: LayerColor, style?: LayerColor): MannerColor;
91
+ static generate(path: string[], seed: ColorThemeSeed, base: LayerColor, style?: LayerColor): MannerColor;
80
92
  }
81
93
  export declare class KindColor extends MannerColor {
82
94
  plain: MannerColor;
@@ -87,7 +99,7 @@ export declare class KindColor extends MannerColor {
87
99
  error: MannerColor;
88
100
  constructor(plain: MannerColor, accent: MannerColor, info: MannerColor, success: MannerColor, warning: MannerColor, error: MannerColor);
89
101
  asCss(): string;
90
- static generate(s: ColorThemeSeed, c: LayerColor): KindColor;
102
+ static generate(path: string[], seed: ColorThemeSeed, base: LayerColor): KindColor;
91
103
  }
92
104
  export declare class SchemeColor extends KindColor {
93
105
  primary: KindColor;
@@ -95,19 +107,27 @@ export declare class SchemeColor extends KindColor {
95
107
  inverse: KindColor;
96
108
  constructor(primary: KindColor, secondary: KindColor, inverse: KindColor);
97
109
  asCss(): string;
98
- static generate(seed: ColorThemeSeed, c: LayerColor): SchemeColor;
110
+ static generate(path: string[], seed: ColorThemeSeed, base: LayerColor): SchemeColor;
99
111
  }
100
112
  export declare class ModeColor extends SchemeColor {
101
113
  light: SchemeColor;
102
114
  dark: SchemeColor;
103
115
  constructor(light: SchemeColor, dark: SchemeColor);
104
116
  asCss(): string;
105
- static generate(seed: ColorThemeSeed): ModeColor;
117
+ static generate(path: string[], seed: ColorThemeSeed): ModeColor;
118
+ }
119
+ export declare class ContrastColor extends ModeColor {
120
+ normal: ModeColor;
121
+ highvis: ModeColor;
122
+ constructor(normal: ModeColor, highvis: ModeColor);
123
+ asCss(): string;
124
+ static generate(path: string[], seed: ColorThemeSeed): ContrastColor;
106
125
  }
107
126
  export declare class ColorTheme {
108
- colors: ColorSeedColors;
109
- color: ModeColor;
110
- constructor(colors: ColorSeedColors, color: ModeColor);
127
+ colors: ThemeColors;
128
+ color: ContrastColor;
129
+ constructor(colors: ThemeColors, color: ContrastColor);
111
130
  asCss(): string;
112
- static generate(seed?: Partial<PartialColorThemeSeed>, highVis?: boolean): ColorTheme;
131
+ static generate(seed?: Partial<PartialColorThemeSeed>): ColorTheme;
113
132
  }
133
+ export {};
@@ -1,6 +1,8 @@
1
1
  import { hslToRgb, rgbToHsl } from "colors-convert";
2
2
  import { clamp } from "../util/util";
3
3
  import { colorThemePreset } from "./color_theme";
4
+ import { lColor, } from "./seed";
5
+ export const cContrasts = ["normal", "highvis"];
4
6
  export const cModes = ["light", "dark"];
5
7
  export const cSchemes = ["primary", "secondary", "inverse"];
6
8
  export const cKinds = [
@@ -159,7 +161,7 @@ export class StateColor extends LayerColor {
159
161
  `${activeS} { ${this.active.asCss()} }\n` +
160
162
  `${disS} { ${this.disabled.asCss()} }`);
161
163
  }
162
- static generate(_, context, style, fromFront) {
164
+ static generate(p, _, context, style, fromFront) {
163
165
  function _make(factor) {
164
166
  const front = style.front;
165
167
  return new LayerColor((fromFront
@@ -192,8 +194,12 @@ export class MannerColor extends StateColor {
192
194
  return ((maj ? ` .major { ${maj.asCss()} } \n` : "") +
193
195
  (min ? ` .minor { ${min.asCss()} } \n` : ""));
194
196
  }
195
- static generate(s, c, style) {
196
- return new MannerColor(style ? StateColor.generate(s, c, s.variant.major(s, c, style)) : null, style ? StateColor.generate(s, c, s.variant.minor(s, c, style)) : null, StateColor.generate(s, c, s.variant.flat(s, c, style), true));
197
+ static generate(path, seed, base, style) {
198
+ return new MannerColor(style
199
+ ? StateColor.generate([...path, "major"], seed, base, seed.variant.major({ path, seed, base, style }))
200
+ : null, style
201
+ ? StateColor.generate([...path, "minor"], seed, base, seed.variant.minor({ path, seed, base, style }))
202
+ : null, StateColor.generate([...path, "flat"], seed, base, seed.variant.flat({ path, seed, base, style }), true));
197
203
  /*
198
204
  if (!style) {
199
205
  return new MannerColor(null, null, StateColor.generate(s, c, c, true));
@@ -226,8 +232,8 @@ export class KindColor extends MannerColor {
226
232
  `.warning { ${this.warning.asCss()} }\n` +
227
233
  `.error { ${this.error.asCss()} }`);
228
234
  }
229
- static generate(s, c) {
230
- return new KindColor(MannerColor.generate(s, c), MannerColor.generate(s, c, s.style.accent(s, c, s.accent)), MannerColor.generate(s, c, s.style.info(s, c, s.info)), MannerColor.generate(s, c, s.style.success(s, c, s.success)), MannerColor.generate(s, c, s.style.warning(s, c, s.warning)), MannerColor.generate(s, c, s.style.error(s, c, s.error)));
235
+ static generate(path, seed, base) {
236
+ return new KindColor(MannerColor.generate([...path, "plain"], seed, base), MannerColor.generate([...path, "accent"], seed, base, seed.style.accent({ path, seed, base, style: lColor(seed.accent) })), MannerColor.generate([...path, "info"], seed, base, seed.style.info({ path, seed, base, style: lColor(seed.info) })), MannerColor.generate([...path, "success"], seed, base, seed.style.success({ path, seed, base, style: lColor(seed.success) })), MannerColor.generate([...path, "warning"], seed, base, seed.style.warning({ path, seed, base, style: lColor(seed.warning) })), MannerColor.generate([...path, "error"], seed, base, seed.style.error({ path, seed, base, style: lColor(seed.error) })));
231
237
  }
232
238
  }
233
239
  export class SchemeColor extends KindColor {
@@ -243,9 +249,9 @@ export class SchemeColor extends KindColor {
243
249
  `.secondary { ${this.secondary.asCss()} }\n` +
244
250
  `.inverse { ${this.inverse.asCss()} }`);
245
251
  }
246
- static generate(seed, c) {
252
+ static generate(path, seed, base) {
247
253
  const m = seed.scheme;
248
- return new SchemeColor(KindColor.generate(seed, m.primary(seed, c)), KindColor.generate(seed, m.secondary(seed, c)), KindColor.generate(seed, m.inverse(seed, c)));
254
+ return new SchemeColor(KindColor.generate([...path, "primary"], seed, m.primary({ path, seed, base })), KindColor.generate([...path, "secondary"], seed, m.secondary({ path, seed, base })), KindColor.generate([...path, "inverse"], seed, m.inverse({ path, seed, base })));
249
255
  }
250
256
  }
251
257
  export class ModeColor extends SchemeColor {
@@ -255,10 +261,24 @@ export class ModeColor extends SchemeColor {
255
261
  this.dark = dark;
256
262
  }
257
263
  asCss() {
258
- return (`.elbe { ${this.light.asCss()}}` + `.elbe.dark { ${this.dark.asCss()}}`);
264
+ return `&{ ${this.light.asCss()}}` + `&.dark { ${this.dark.asCss()}}`;
259
265
  }
260
- static generate(seed) {
261
- return new ModeColor(SchemeColor.generate(seed, seed.base), SchemeColor.generate(seed, seed.mode.dark(seed, seed.base)));
266
+ static generate(path, seed) {
267
+ return new ModeColor(SchemeColor.generate(path, seed, lColor(seed.base)), SchemeColor.generate([...path, "dark"], seed, seed.mode.dark({ path, seed, base: lColor(seed.base) })));
268
+ }
269
+ }
270
+ export class ContrastColor extends ModeColor {
271
+ constructor(normal, highvis) {
272
+ super(normal.light, normal.dark);
273
+ this.normal = normal;
274
+ this.highvis = highvis;
275
+ }
276
+ asCss() {
277
+ return (`& { ${this.normal.asCss()}}` + `&.highvis { ${this.highvis.asCss()}}`);
278
+ }
279
+ static generate(path, seed) {
280
+ const p = { path, base: lColor(seed.base), seed };
281
+ return new ContrastColor(ModeColor.generate(path, seed.contrast.normal(p)), ModeColor.generate([...path, "highvis"], seed.contrast.highvis(p)));
262
282
  }
263
283
  }
264
284
  export class ColorTheme {
@@ -267,17 +287,24 @@ export class ColorTheme {
267
287
  this.color = color;
268
288
  }
269
289
  asCss() {
270
- return (`:root {
290
+ return `
271
291
  --c-accent: ${this.colors.accent.back.asCss()};
272
292
  --c-info: ${this.colors.info.back.asCss()};
273
293
  --c-success: ${this.colors.success.back.asCss()};
274
294
  --c-warning: ${this.colors.warning.back.asCss()};
275
295
  --c-error: ${this.colors.error.back.asCss()};
276
- }` + this.color.asCss());
296
+ ${this.color.asCss()}`;
277
297
  }
278
- static generate(seed, highVis) {
279
- const s = colorThemePreset(seed, highVis !== null && highVis !== void 0 ? highVis : false);
280
- return new ColorTheme(s, ModeColor.generate(s));
298
+ static generate(seed) {
299
+ const s = colorThemePreset(seed);
300
+ return new ColorTheme({
301
+ base: lColor(s.base),
302
+ accent: lColor(s.accent),
303
+ info: lColor(s.info),
304
+ success: lColor(s.success),
305
+ warning: lColor(s.warning),
306
+ error: lColor(s.error),
307
+ }, ContrastColor.generate([], s));
281
308
  }
282
309
  }
283
310
  //Bun.write("./example.css", ColorTheme.generate().asCss());
@@ -6,12 +6,10 @@ export class GeometryTheme {
6
6
  this.borderWidth = borderWidth;
7
7
  }
8
8
  asCss() {
9
- return (`:root{` +
10
- `--g-size: ${this.size}px;` +
9
+ return (`--g-size: ${this.size}px;` +
11
10
  `--g-radius: ${this.radius}rem;` +
12
11
  `--g-padding: ${this.padding}rem;` +
13
- `--g-border-width: ${this.borderWidth}rem;` +
14
- "}");
12
+ `--g-border-width: ${this.borderWidth}rem;`);
15
13
  }
16
14
  static generate(seed) {
17
15
  const s = geometryThemePreset(seed);
@@ -1,11 +1,18 @@
1
- import { LayerColor, SeedFlatSelector, SeedSelector, SeedStyleSelector } from "../..";
2
- export type ColorSeedColors = {
3
- base: LayerColor;
4
- accent: LayerColor;
5
- info: LayerColor;
6
- success: LayerColor;
7
- warning: LayerColor;
8
- error: LayerColor;
1
+ import { LayerColor, SeedFlatSelector, SeedModifier, SeedSelector, SeedStyleSelector } from "../..";
2
+ export type HexColor = `#${string}`;
3
+ export type ColorSeedColors = _Colors<LayerColor | HexColor>;
4
+ export type ThemeColors = _Colors<LayerColor>;
5
+ type _Colors<T> = {
6
+ base: T;
7
+ accent: T;
8
+ info: T;
9
+ success: T;
10
+ warning: T;
11
+ error: T;
12
+ };
13
+ type _ContrastSeed = {
14
+ highvis: SeedModifier;
15
+ normal: SeedModifier;
9
16
  };
10
17
  type _ModeSeed = {
11
18
  light: SeedSelector;
@@ -29,15 +36,18 @@ type _VariantSeed = {
29
36
  flat: SeedFlatSelector;
30
37
  };
31
38
  export type ColorThemeSeed = ColorSeedColors & {
39
+ contrast: _ContrastSeed;
32
40
  mode: _ModeSeed;
33
41
  scheme: _SchemeSeed;
34
42
  style: _StyleSeed;
35
43
  variant: _VariantSeed;
36
44
  };
37
45
  export type PartialColorThemeSeed = Partial<ColorSeedColors> & {
46
+ contrast?: Partial<_ContrastSeed>;
38
47
  mode?: Partial<_ModeSeed>;
39
48
  scheme?: Partial<_SchemeSeed>;
40
49
  style?: Partial<_StyleSeed>;
41
50
  variant?: Partial<_VariantSeed>;
42
51
  };
52
+ export declare function lColor(color: LayerColor | HexColor): LayerColor;
43
53
  export {};
@@ -1 +1,4 @@
1
- export {};
1
+ import { LayerColor, } from "../..";
2
+ export function lColor(color) {
3
+ return typeof color === "string" ? LayerColor.fromHex(color) : color;
4
+ }
@@ -2,6 +2,7 @@ import "../../elbe.css";
2
2
  import { ColorTheme } from "./colors";
3
3
  import { GeometryTheme, type GeometryThemeSeed } from "./geometry_theme";
4
4
  import { PartialColorThemeSeed } from "./seed";
5
+ import { ElbeThemeConfig } from "./theme_context";
5
6
  import { TypeTheme, type TypeThemeSeed } from "./type_theme";
6
7
  export * from "./color_theme";
7
8
  export * from "./colors";
@@ -18,15 +19,12 @@ export declare class ElbeThemeData {
18
19
  readonly geometry: GeometryTheme;
19
20
  constructor(color: ColorTheme, type: TypeTheme, geometry: GeometryTheme);
20
21
  asCss(): string;
21
- _pageBackground(dark?: boolean): string;
22
- static fromSeed(seed: ElbeThemeSeed, highVis: boolean): ElbeThemeData;
22
+ static fromSeed(seed: ElbeThemeSeed): ElbeThemeData;
23
23
  }
24
24
  export declare function ElbeTheme(p: {
25
25
  children: any;
26
- dark?: boolean;
27
26
  todoOverlay?: boolean;
28
- highVis?: boolean;
29
- } & ({
27
+ } & Partial<ElbeThemeConfig> & ({
30
28
  theme: ElbeThemeData;
31
29
  } | {
32
30
  seed?: ElbeThemeSeed;
@@ -3,6 +3,7 @@ import "../../elbe.css";
3
3
  import { ToDo } from "../components/dev/todo";
4
4
  import { ColorTheme } from "./colors";
5
5
  import { GeometryTheme } from "./geometry_theme";
6
+ import { _configCss, makeThemeConfig, ThemeConfigContext, ThemeContext, } from "./theme_context";
6
7
  import { TypeTheme } from "./type_theme";
7
8
  export * from "./color_theme";
8
9
  export * from "./colors";
@@ -15,22 +16,18 @@ export class ElbeThemeData {
15
16
  this.geometry = geometry;
16
17
  }
17
18
  asCss() {
18
- return [this.color, this.type, this.geometry]
19
+ const inner = [this.color, this.type, this.geometry]
19
20
  .map((s) => s.asCss())
20
21
  .join("\n");
22
+ return `.elbe {${inner}}`;
21
23
  }
22
- _pageBackground(dark) {
23
- const c = this.color.color;
24
- return `html,:root {${(!!dark ? c.dark : c.light).asCss()};`;
25
- }
26
- static fromSeed(seed, highVis) {
27
- return new ElbeThemeData(ColorTheme.generate(seed.color, highVis), TypeTheme.generate(seed.type), GeometryTheme.generate(seed.geometry));
24
+ static fromSeed(seed) {
25
+ return new ElbeThemeData(ColorTheme.generate(seed.color), TypeTheme.generate(seed.type), GeometryTheme.generate(seed.geometry));
28
26
  }
29
27
  }
30
28
  export function ElbeTheme(p) {
31
- var _a, _b;
32
- const theme = "theme" in p
33
- ? p.theme
34
- : ElbeThemeData.fromSeed((_a = p.seed) !== null && _a !== void 0 ? _a : {}, (_b = p.highVis) !== null && _b !== void 0 ? _b : false);
35
- return (_jsxs("div", { class: `elbe ${p.dark ? "dark" : ""} ${p.highVis ? "high_vis" : ""}`, children: [p.todoOverlay && _jsx(ToDo.Overlay, {}), _jsx("style", { children: theme.asCss() }), _jsx("style", { children: theme._pageBackground(p.dark) }), p.children] }));
29
+ var _a;
30
+ const theme = "theme" in p ? p.theme : ElbeThemeData.fromSeed((_a = p.seed) !== null && _a !== void 0 ? _a : {});
31
+ const config = makeThemeConfig(p);
32
+ return (_jsxs("div", { class: `elbe ${config.dark ? "dark" : ""} ${config.highVis ? "highvis" : ""} ${config.reducedMotion ? "reduced_motion" : ""}`, children: [p.todoOverlay && _jsx(ToDo.Overlay, {}), _jsx("style", { children: theme.asCss() }), _jsx("style", { children: _configCss(theme, config) }), _jsx(ThemeConfigContext.Provider, { value: config, children: _jsx(ThemeContext.Provider, { value: theme, children: p.children }) })] }));
36
33
  }
@@ -0,0 +1,18 @@
1
+ import { ElbeThemeData } from "./theme";
2
+ export interface ElbeThemeConfig {
3
+ highVis: boolean;
4
+ dark: boolean;
5
+ reducedMotion: boolean;
6
+ scale: number;
7
+ }
8
+ export declare function makeThemeConfig(p: Partial<ElbeThemeConfig>): {
9
+ highVis: boolean;
10
+ dark: boolean;
11
+ reducedMotion: boolean;
12
+ scale: number;
13
+ };
14
+ export declare const ThemeConfigContext: import("preact").Context<ElbeThemeConfig>;
15
+ export declare function _configCss(t: ElbeThemeData, c: ElbeThemeConfig): string;
16
+ export declare const ThemeContext: import("preact").Context<ElbeThemeData>;
17
+ export declare function useThemeConfig(): ElbeThemeConfig;
18
+ export declare function useTheme(): ElbeThemeData;
@@ -0,0 +1,26 @@
1
+ import { createContext } from "preact";
2
+ import { useContext } from "preact/hooks";
3
+ import { ElbeThemeData } from "./theme";
4
+ export function makeThemeConfig(p) {
5
+ var _a, _b, _c, _d;
6
+ return {
7
+ highVis: (_a = p.highVis) !== null && _a !== void 0 ? _a : false,
8
+ dark: (_b = p.dark) !== null && _b !== void 0 ? _b : false,
9
+ reducedMotion: (_c = p.reducedMotion) !== null && _c !== void 0 ? _c : false,
10
+ scale: (_d = p.scale) !== null && _d !== void 0 ? _d : 1,
11
+ };
12
+ }
13
+ export const ThemeConfigContext = createContext(makeThemeConfig({}));
14
+ export function _configCss(t, c) {
15
+ const cBack = c.dark ? t.color.color.dark : t.color.color.light;
16
+ return `html,:root {
17
+ font-size: ${c.scale * 16}px;
18
+ ${cBack.asCss()};}`;
19
+ }
20
+ export const ThemeContext = createContext(ElbeThemeData.fromSeed({}));
21
+ export function useThemeConfig() {
22
+ return useContext(ThemeConfigContext);
23
+ }
24
+ export function useTheme() {
25
+ return useContext(ThemeContext);
26
+ }
@@ -51,7 +51,7 @@ export function typeThemePreset(merge) {
51
51
  return Object.assign({ heading: {
52
52
  bold: true,
53
53
  family: ["Calistoga", "Arial", "sans-serif"],
54
- size: 2.2,
54
+ size: 2,
55
55
  }, body: {
56
56
  bold: false,
57
57
  family: ["Helvetica", "Arial", "sans-serif"],
@@ -61,7 +61,10 @@ export function typeThemePreset(merge) {
61
61
  family: ["Courier", "monospace"],
62
62
  size: 1,
63
63
  }, headingVariants: (style, variant) => {
64
- const size = style.size * (1 - (variant - 1) * 0.1);
64
+ const reg = 1;
65
+ const diff = style.size - reg;
66
+ const varFac = (4 - (variant - 2)) / 6;
67
+ const size = reg + diff * Math.pow(varFac, 1.5);
65
68
  return Object.assign(Object.assign({}, style), { size });
66
69
  }, bodyVariants: (style, variant) => {
67
70
  const size = style.size * (variant === "s" ? 0.8 : 1.2);
@@ -0,0 +1,8 @@
1
+ import React from "preact/compat";
2
+ type _ToolbarState = "toolbar" | "overflow" | null;
3
+ export declare const ToolbarContext: React.Context<_ToolbarState>;
4
+ export declare function useToolbar(): {
5
+ isInToolbar: boolean;
6
+ isInOverflow: boolean;
7
+ };
8
+ export {};
@@ -0,0 +1,9 @@
1
+ import React, { useContext } from "preact/compat";
2
+ export const ToolbarContext = React.createContext(null);
3
+ export function useToolbar() {
4
+ const ctx = useContext(ToolbarContext);
5
+ return {
6
+ isInToolbar: ctx === "toolbar",
7
+ isInOverflow: ctx === "overflow",
8
+ };
9
+ }
@@ -0,0 +1,35 @@
1
+ import React from "preact/compat";
2
+ import { ElbeChildren } from "../types";
3
+ type _LocaleID = `${string}_${string}` | `${string}`;
4
+ type _LocaleIDEasy = `${string}_${string}_easy` | _LocaleID;
5
+ type _L10nData = {
6
+ [key: string]: any;
7
+ };
8
+ export interface _L10nState<T extends _L10nData> {
9
+ /** the current locale */
10
+ c: T;
11
+ locale: string;
12
+ easyLang: boolean;
13
+ setLocale: (locale: string) => void;
14
+ setEasyLang: (easyLang: boolean) => void;
15
+ }
16
+ type _L10nProps = {
17
+ children: ElbeChildren;
18
+ initialLocale?: _LocaleID;
19
+ initialEasyLang?: boolean;
20
+ };
21
+ /**
22
+ * L10nBase is a function that creates a localization context provider and a hook to use the localization context.
23
+ * @param fallback this is the fallback locale, which is used if no other locale is found. It is the basis for the locale type and must thus be complete.
24
+ * @param supported this is a list of supported locales. It is a map of locale IDs to partial localization data. The keys are the locale IDs, and the values are the localization data.
25
+ * @returns an object with the L10n component and the useL10n hook.
26
+ */
27
+ export declare function makeL10n<T extends _L10nData>(fallback: {
28
+ [locale: _LocaleIDEasy]: T;
29
+ }, supported: {
30
+ [locale: _LocaleIDEasy]: Partial<T>;
31
+ }): {
32
+ L10n: React.FunctionComponent<_L10nProps>;
33
+ useL10n: () => _L10nState<T>;
34
+ };
35
+ export {};
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ import { createContext } from "preact";
3
+ import { useContext, useEffect, useState } from "preact/hooks";
4
+ const _L10nContext = createContext(null);
5
+ function _useL10n() {
6
+ const ctx = useContext(_L10nContext);
7
+ if (!ctx)
8
+ throw new Error("useL10n must be used within a L10n");
9
+ return ctx;
10
+ }
11
+ /**
12
+ * L10nBase is a function that creates a localization context provider and a hook to use the localization context.
13
+ * @param fallback this is the fallback locale, which is used if no other locale is found. It is the basis for the locale type and must thus be complete.
14
+ * @param supported this is a list of supported locales. It is a map of locale IDs to partial localization data. The keys are the locale IDs, and the values are the localization data.
15
+ * @returns an object with the L10n component and the useL10n hook.
16
+ */
17
+ export function makeL10n(fallback, supported) {
18
+ if (Object.keys(supported).length === 0) {
19
+ throw new Error("L10nBase: No fallback locales provided");
20
+ }
21
+ const fallbackLocale = Object.keys(fallback)[0];
22
+ const fallbackValue = fallback[fallbackLocale];
23
+ const supportedLocales = Object.assign(Object.assign({}, supported), fallback);
24
+ const useL10n = (_useL10n);
25
+ const L10n = (p) => {
26
+ var _a, _b;
27
+ const [loc, setLoc] = useState((_a = p.initialLocale) !== null && _a !== void 0 ? _a : navigator.language);
28
+ const [easy, setEasy] = useState((_b = p.initialEasyLang) !== null && _b !== void 0 ? _b : false);
29
+ const [currentLang, setCurrentLang] = useState(fallbackValue);
30
+ useEffect(() => {
31
+ var _a;
32
+ const match = _bestMatch(loc, easy, Object.keys(supportedLocales));
33
+ setCurrentLang(Object.assign(Object.assign({}, fallbackValue), ((_a = supportedLocales[match !== null && match !== void 0 ? match : ""]) !== null && _a !== void 0 ? _a : {})));
34
+ }, [loc, easy]);
35
+ return (_jsx(_L10nContext.Provider, { value: {
36
+ c: currentLang,
37
+ locale: loc,
38
+ easyLang: easy,
39
+ setLocale: (l) => setLoc(l),
40
+ setEasyLang: (e) => setEasy(e),
41
+ }, children: p.children }));
42
+ };
43
+ return { L10n, useL10n };
44
+ }
45
+ function _locale(l) {
46
+ const parts = l.split("_");
47
+ const easy = parts.length > 2 && parts[2] === "easy";
48
+ const region = parts.length > 1 ? parts[1].toLowerCase() : null;
49
+ const lang = parts[0].toLowerCase();
50
+ return { full: l, lang, region, easy };
51
+ }
52
+ function _bestMatch(locale, easy, locales) {
53
+ const loc = _locale(locale.replaceAll("-", "_"));
54
+ const asLocales = locales.map((l) => _locale(l));
55
+ // filter by language
56
+ const langMatch = asLocales.filter((l) => loc.lang === l.lang);
57
+ // filter by easy
58
+ const easyMatch = langMatch.filter((l) => l.easy === easy);
59
+ // filter by region
60
+ const regionMatch = easyMatch.filter((l) => l.region === loc.region);
61
+ if (regionMatch.length > 0)
62
+ return regionMatch[0].full;
63
+ if (easyMatch.length > 0)
64
+ return easyMatch[0].full;
65
+ if (langMatch.length > 0)
66
+ return langMatch[0].full;
67
+ return null;
68
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @see https://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union/53955431
3
+ */
4
+ type IsSingleton<T> = [T] extends [UnionToIntersection<T>] ? true : false;
5
+ /**
6
+ * @author https://stackoverflow.com/users/2887218/jcalz
7
+ * @see https://stackoverflow.com/a/50375286/10325032
8
+ */
9
+ type UnionToIntersection<Union> = (Union extends any ? (argument: Union) => void : never) extends (argument: infer Intersection) => void ? Intersection : never;
10
+ export type SingletonOnly<T> = IsSingleton<T> extends true ? T : never;
11
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export type int = number;
2
+ export type float = number;
3
+ /** a unix epoch timestamp in milliseconds */
4
+ export type UnixMS = int;
5
+ export type ElbeChild = React.ReactNode;
6
+ export type ElbeChildren = ElbeChild[] | ElbeChild;
7
+ export type s = string;
8
+ export type n = number;
9
+ export type b = boolean;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,4 @@
1
- import type React from "preact/compat";
2
- export type ElbeChild = React.ReactNode;
3
- export type ElbeChildren = ElbeChild[] | ElbeChild;
1
+ import { int } from "../..";
4
2
  export declare function clamp(value: number, min: number, max: number): number;
5
3
  export declare function classString(classes: (string | false | null | undefined)[]): string;
6
4
  /**
@@ -20,3 +18,7 @@ export declare function share(data: {
20
18
  */
21
19
  export declare function copyToClipboard(text: string, toastMsg?: string): void;
22
20
  export declare function scrollToId(id: string): void;
21
+ export declare function randomAlphaNum(length: int, prefix?: string): string;
22
+ export type LayoutModes = "mobile" | "narrow" | "wide";
23
+ export declare function useLayoutMode(): LayoutModes;
24
+ export declare function useSiteScroll(): number;
@@ -1,4 +1,5 @@
1
- import { showToast } from "./toast";
1
+ import { useEffect, useState } from "preact/compat";
2
+ import { showToast } from "../..";
2
3
  export function clamp(value, min, max) {
3
4
  return Math.min(Math.max(value, min), max);
4
5
  }
@@ -36,3 +37,37 @@ export function scrollToId(id) {
36
37
  if (el)
37
38
  el.scrollIntoView({ behavior: "smooth" });
38
39
  }
40
+ export function randomAlphaNum(length, prefix = "") {
41
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
42
+ let result = prefix;
43
+ for (let i = 0; i < length; i++) {
44
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
45
+ }
46
+ return result;
47
+ }
48
+ function _layoutMode() {
49
+ const w = window.innerWidth;
50
+ if (w < 700)
51
+ return "mobile";
52
+ if (w < 1100)
53
+ return "narrow";
54
+ return "wide";
55
+ }
56
+ export function useLayoutMode() {
57
+ const [mode, setMode] = useState(_layoutMode());
58
+ useEffect(() => {
59
+ const onResize = () => setMode(_layoutMode());
60
+ window.addEventListener("resize", onResize);
61
+ return () => window.removeEventListener("resize", onResize);
62
+ });
63
+ return mode;
64
+ }
65
+ export function useSiteScroll() {
66
+ const [scroll, setScroll] = useState(0);
67
+ useEffect(() => {
68
+ const onScroll = () => setScroll(window.scrollY);
69
+ window.addEventListener("scroll", onScroll);
70
+ return () => window.removeEventListener("scroll", onScroll);
71
+ }, []);
72
+ return scroll;
73
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elbe-ui",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "author": "Robin Naumann",
5
5
  "license": "MIT",
6
6
  "repository": {