@rvx/ui 0.1.6

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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/dist/common/events.d.ts +72 -0
  3. package/dist/common/events.js +58 -0
  4. package/dist/common/events.js.map +1 -0
  5. package/dist/common/parsers.d.ts +88 -0
  6. package/dist/common/parsers.js +62 -0
  7. package/dist/common/parsers.js.map +1 -0
  8. package/dist/common/theme-test.d.ts +7 -0
  9. package/dist/common/theme-test.js +14 -0
  10. package/dist/common/theme-test.js.map +1 -0
  11. package/dist/common/theme.d.ts +144 -0
  12. package/dist/common/theme.js +2 -0
  13. package/dist/common/theme.js.map +1 -0
  14. package/dist/common/trim.d.ts +12 -0
  15. package/dist/common/trim.js +16 -0
  16. package/dist/common/trim.js.map +1 -0
  17. package/dist/common/types.d.ts +13 -0
  18. package/dist/common/types.js +10 -0
  19. package/dist/common/types.js.map +1 -0
  20. package/dist/common/writing-mode.d.ts +82 -0
  21. package/dist/common/writing-mode.js +61 -0
  22. package/dist/common/writing-mode.js.map +1 -0
  23. package/dist/components/button.d.ts +42 -0
  24. package/dist/components/button.js +26 -0
  25. package/dist/components/button.js.map +1 -0
  26. package/dist/components/checkbox.d.ts +9 -0
  27. package/dist/components/checkbox.js +32 -0
  28. package/dist/components/checkbox.js.map +1 -0
  29. package/dist/components/collapse-test.d.ts +8 -0
  30. package/dist/components/collapse-test.js +15 -0
  31. package/dist/components/collapse-test.js.map +1 -0
  32. package/dist/components/collapse.d.ts +13 -0
  33. package/dist/components/collapse.js +44 -0
  34. package/dist/components/collapse.js.map +1 -0
  35. package/dist/components/column.d.ts +12 -0
  36. package/dist/components/column.js +12 -0
  37. package/dist/components/column.js.map +1 -0
  38. package/dist/components/control-group.d.ts +7 -0
  39. package/dist/components/control-group.js +11 -0
  40. package/dist/components/control-group.js.map +1 -0
  41. package/dist/components/dialog.d.ts +33 -0
  42. package/dist/components/dialog.js +67 -0
  43. package/dist/components/dialog.js.map +1 -0
  44. package/dist/components/dropdown-input.d.ts +27 -0
  45. package/dist/components/dropdown-input.js +31 -0
  46. package/dist/components/dropdown-input.js.map +1 -0
  47. package/dist/components/dropdown.d.ts +123 -0
  48. package/dist/components/dropdown.js +176 -0
  49. package/dist/components/dropdown.js.map +1 -0
  50. package/dist/components/flex-space.d.ts +4 -0
  51. package/dist/components/flex-space.js +10 -0
  52. package/dist/components/flex-space.js.map +1 -0
  53. package/dist/components/heading.d.ts +9 -0
  54. package/dist/components/heading.js +14 -0
  55. package/dist/components/heading.js.map +1 -0
  56. package/dist/components/label.d.ts +14 -0
  57. package/dist/components/label.js +15 -0
  58. package/dist/components/label.js.map +1 -0
  59. package/dist/components/layer.d.ts +81 -0
  60. package/dist/components/layer.js +164 -0
  61. package/dist/components/layer.js.map +1 -0
  62. package/dist/components/link.d.ts +57 -0
  63. package/dist/components/link.js +26 -0
  64. package/dist/components/link.js.map +1 -0
  65. package/dist/components/page.d.ts +9 -0
  66. package/dist/components/page.js +17 -0
  67. package/dist/components/page.js.map +1 -0
  68. package/dist/components/popout.d.ts +134 -0
  69. package/dist/components/popout.js +259 -0
  70. package/dist/components/popout.js.map +1 -0
  71. package/dist/components/popover.d.ts +139 -0
  72. package/dist/components/popover.js +101 -0
  73. package/dist/components/popover.js.map +1 -0
  74. package/dist/components/radio-buttons.d.ts +17 -0
  75. package/dist/components/radio-buttons.js +26 -0
  76. package/dist/components/radio-buttons.js.map +1 -0
  77. package/dist/components/row.d.ts +10 -0
  78. package/dist/components/row.js +23 -0
  79. package/dist/components/row.js.map +1 -0
  80. package/dist/components/scroll-view.d.ts +6 -0
  81. package/dist/components/scroll-view.js +72 -0
  82. package/dist/components/scroll-view.js.map +1 -0
  83. package/dist/components/text-input.d.ts +53 -0
  84. package/dist/components/text-input.js +35 -0
  85. package/dist/components/text-input.js.map +1 -0
  86. package/dist/components/text.d.ts +7 -0
  87. package/dist/components/text.js +11 -0
  88. package/dist/components/text.js.map +1 -0
  89. package/dist/components/validation.d.ts +109 -0
  90. package/dist/components/validation.js +151 -0
  91. package/dist/components/validation.js.map +1 -0
  92. package/dist/components/value.d.ts +7 -0
  93. package/dist/components/value.js +11 -0
  94. package/dist/components/value.js.map +1 -0
  95. package/dist/index.d.ts +29 -0
  96. package/dist/index.js +30 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/test.d.ts +2 -0
  99. package/dist/test.js +3 -0
  100. package/dist/test.js.map +1 -0
  101. package/dist/theme.module.css +679 -0
  102. package/dist/theme.module.css.map +1 -0
  103. package/package.json +29 -0
  104. package/src/common/events.tsx +130 -0
  105. package/src/common/parsers.tsx +167 -0
  106. package/src/common/theme-test.tsx +20 -0
  107. package/src/common/theme.tsx +165 -0
  108. package/src/common/trim.tsx +30 -0
  109. package/src/common/types.tsx +23 -0
  110. package/src/common/writing-mode.tsx +150 -0
  111. package/src/components/button.tsx +94 -0
  112. package/src/components/checkbox.tsx +64 -0
  113. package/src/components/collapse-test.tsx +23 -0
  114. package/src/components/collapse.tsx +75 -0
  115. package/src/components/column.tsx +28 -0
  116. package/src/components/control-group.tsx +22 -0
  117. package/src/components/dialog.tsx +137 -0
  118. package/src/components/dropdown-input.tsx +82 -0
  119. package/src/components/dropdown.tsx +352 -0
  120. package/src/components/flex-space.tsx +15 -0
  121. package/src/components/heading.tsx +23 -0
  122. package/src/components/label.tsx +37 -0
  123. package/src/components/layer.tsx +299 -0
  124. package/src/components/link.tsx +118 -0
  125. package/src/components/page.tsx +36 -0
  126. package/src/components/popout.tsx +461 -0
  127. package/src/components/popover.tsx +292 -0
  128. package/src/components/radio-buttons.tsx +81 -0
  129. package/src/components/row.tsx +37 -0
  130. package/src/components/scroll-view.tsx +97 -0
  131. package/src/components/text-input.tsx +117 -0
  132. package/src/components/text.tsx +22 -0
  133. package/src/components/validation.tsx +272 -0
  134. package/src/components/value.tsx +22 -0
  135. package/src/index.tsx +29 -0
  136. package/src/test.tsx +2 -0
  137. package/src/theme/base.scss +69 -0
  138. package/src/theme/common.scss +51 -0
  139. package/src/theme/components/button.scss +116 -0
  140. package/src/theme/components/checkbox.scss +25 -0
  141. package/src/theme/components/collapse.scss +64 -0
  142. package/src/theme/components/column.scss +28 -0
  143. package/src/theme/components/control-group.scss +14 -0
  144. package/src/theme/components/dialog.scss +44 -0
  145. package/src/theme/components/dropdown.scss +50 -0
  146. package/src/theme/components/flex-space.scss +6 -0
  147. package/src/theme/components/heading.scss +39 -0
  148. package/src/theme/components/label.scss +24 -0
  149. package/src/theme/components/link.scss +25 -0
  150. package/src/theme/components/page.scss +22 -0
  151. package/src/theme/components/popover.scss +58 -0
  152. package/src/theme/components/radio-buttons.scss +31 -0
  153. package/src/theme/components/row.scss +17 -0
  154. package/src/theme/components/scroll-view.scss +51 -0
  155. package/src/theme/components/text-input.scss +45 -0
  156. package/src/theme/components/text.scss +12 -0
  157. package/src/theme/components/validation.scss +15 -0
  158. package/src/theme/components/value.scss +4 -0
  159. package/src/theme/theme.scss +22 -0
@@ -0,0 +1,272 @@
1
+ import { ClassValue, ContextKey, Emitter, Event, Expression, extract, For, map, sig, Signal, StyleValue, teardown, trigger, TriggerPipe, uniqueId, untrack } from "rvx";
2
+ import { TaskSlot } from "rvx/async";
3
+
4
+ import { THEME } from "../common/theme.js";
5
+ import { Collapse } from "./collapse.js";
6
+ import { Text } from "./text.js";
7
+
8
+ const VALIDATORS = new WeakMap<object, Validator>();
9
+
10
+ /**
11
+ * Context key for validation options used by new validators.
12
+ */
13
+ export const VALIDATION = Symbol.for("rvx-ui:validation") as ContextKey<ValidationOptions>;
14
+
15
+ /**
16
+ * Defines when accessed signals trigger automatic validation.
17
+ *
18
+ * + **if-validated** - **Default.** Validate if validation did run before.
19
+ * + **if-invalid** - Validate if currently invalid.
20
+ * + **never** - Never validate automatically.
21
+ */
22
+ export type ValidationSignalTrigger = "if-validated" | "if-invalid" | "never";
23
+
24
+ export interface ValidationOptions {
25
+ signalTrigger?: ValidationSignalTrigger;
26
+ }
27
+
28
+ export class Validator {
29
+ #slot = new TaskSlot();
30
+ #signalTrigger: ValidationSignalTrigger;
31
+ #rules = sig<ValidationRuleEntry[]>([]);
32
+ #invalid = sig(false);
33
+ #cycle = 0;
34
+
35
+ constructor() {
36
+ const options = extract(VALIDATION);
37
+ this.#signalTrigger = options?.signalTrigger ?? "if-validated";
38
+ }
39
+
40
+ /**
41
+ * Reactively get all validation rule entries.
42
+ */
43
+ rules = (): readonly ValidationRuleEntry[] => this.#rules.value;
44
+
45
+ /**
46
+ * An expression to get a space separated list of error message ids.
47
+ */
48
+ errorMessageIds = (): string => this.#rules.value.map(r => r.id).join(" ");
49
+
50
+ /**
51
+ * An expression to check if this validator is in an invalid state.
52
+ */
53
+ invalid = (): boolean => this.#invalid.value;
54
+
55
+ #addRule(rule: ValidationRule, add: (rules: ValidationRuleEntry[], entry: ValidationRuleEntry) => void): void {
56
+ const entry = new ValidationRuleEntry(rule, trigger(this.#ruleUpdated));
57
+ this.#rules.update(rules => {
58
+ add(rules, entry);
59
+ });
60
+ teardown(() => {
61
+ this.#rules.update(rules => {
62
+ const index = rules.indexOf(entry);
63
+ if (index < 0) {
64
+ return false;
65
+ }
66
+ rules.splice(index, 1);
67
+ });
68
+ });
69
+ }
70
+
71
+ #ruleUpdated = () => {
72
+ const signalTrigger = this.#signalTrigger;
73
+ if (signalTrigger === "never" || (signalTrigger === "if-invalid" && !this.#invalid.value)) {
74
+ return;
75
+ }
76
+ this.triggerValidation();
77
+ };
78
+
79
+ /**
80
+ * Add a rule to be validated first until the current lifecycle is disposed.
81
+ */
82
+ prependRule(rule: ValidationRule): void {
83
+ this.#addRule(rule, (rules, entry) => rules.unshift(entry));
84
+ }
85
+
86
+ /**
87
+ * Add a rule to be validated last until the current lifecycle is disposed.
88
+ */
89
+ appendRule(rule: ValidationRule): void {
90
+ this.#addRule(rule, (rules, entry) => rules.push(entry));
91
+ }
92
+
93
+ /**
94
+ * Attach this validator to an object.
95
+ */
96
+ attach(target: object): void {
97
+ VALIDATORS.set(target, this);
98
+ }
99
+
100
+ async #validate(sideEffect: boolean, signal?: AbortSignal): Promise<boolean> {
101
+ this.#cycle++;
102
+ const rules = untrack(() => this.#rules.value);
103
+ for (let i = 0; i < rules.length; i++) {
104
+ if (signal?.aborted) {
105
+ return false;
106
+ }
107
+ const { validate, visible, alert, updated } = rules[i];
108
+ const valid = await updated(() => validate(signal));
109
+ if (valid) {
110
+ visible.value = false;
111
+ } else {
112
+ const wasVisible = visible.value;
113
+ visible.value = true;
114
+ for (let r = i + 1; r < rules.length; r++) {
115
+ rules[r].visible.value = false;
116
+ }
117
+ this.#invalid.value = true;
118
+ if (wasVisible && !sideEffect) {
119
+ alert.emit();
120
+ }
121
+ return false;
122
+ }
123
+ }
124
+ this.#invalid.value = false;
125
+ return true;
126
+ }
127
+
128
+ /**
129
+ * Validate using the currently attached rules.
130
+ */
131
+ async validate(signal?: AbortSignal): Promise<boolean> {
132
+ return this.#slot.block(() => this.#validate(false, signal));
133
+ }
134
+
135
+ /**
136
+ * Trigger validation as a side effect.
137
+ */
138
+ triggerValidation(): void {
139
+ this.#slot.sideEffect(signal => this.#validate(true, signal));
140
+ }
141
+
142
+ /**
143
+ * Reset this validator to it's initial state.
144
+ */
145
+ reset(): void {
146
+ this.#cycle++;
147
+ this.#invalid.value = false;
148
+ const rules = untrack(() => this.#rules.value);
149
+ for (let i = 0; i < rules.length; i++) {
150
+ rules[i].visible.value = false;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Get or attach a validator to an object.
156
+ */
157
+ static attach(target: object): Validator {
158
+ let validator = VALIDATORS.get(target);
159
+ if (!validator) {
160
+ validator = new Validator();
161
+ VALIDATORS.set(target, validator);
162
+ }
163
+ return validator;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Get the validator attached to the specified target.
169
+ */
170
+ export function validatorFor(target: object): Validator | undefined {
171
+ return VALIDATORS.get(target);
172
+ }
173
+
174
+ export interface ValidationRule {
175
+ validate(signal?: AbortSignal): boolean | Promise<boolean>;
176
+ message: unknown;
177
+ }
178
+
179
+ export class ValidationRuleEntry {
180
+ readonly id = uniqueId();
181
+ readonly visible = sig(false);
182
+ readonly alert = new Emitter<[]>();
183
+ readonly message: unknown;
184
+ readonly validate: ValidationRule["validate"];
185
+ readonly updated: TriggerPipe;
186
+
187
+ constructor(rule: ValidationRule, updated: TriggerPipe) {
188
+ this.message = rule.message;
189
+ this.validate = rule.validate.bind(rule);
190
+ this.updated = updated;
191
+ }
192
+ }
193
+
194
+ export interface ValidateFn<T> {
195
+ (value: T, signal?: AbortSignal): boolean | Promise<boolean>;
196
+ }
197
+
198
+ /**
199
+ * Prepend a validation rule to the validator attached to the specified signal.
200
+ *
201
+ * This is a shorthand, meant to be used with `signal.pipe`.
202
+ *
203
+ * @param target The signal to attach the validator to.
204
+ * @param validate A function to validate the current value. Immediately accessed signals may trigger re-validation when updated.
205
+ * @param message The validation message to show when invalid.
206
+ * @returns The target itself.
207
+ */
208
+ export function rule<T>(target: Signal<T>, validate: ValidateFn<T>, message: unknown): Signal<T> {
209
+ Validator.attach(target).prependRule({
210
+ validate(signal) {
211
+ return validate(target.value, signal);
212
+ },
213
+ message,
214
+ });
215
+ return target;
216
+ }
217
+
218
+ /**
219
+ * Validate all specified targets in parallel.
220
+ */
221
+ export async function validate(...targets: object[]): Promise<boolean> {
222
+ const tasks: Promise<boolean>[] = [];
223
+ for (const target of targets) {
224
+ const validator = validatorFor(target);
225
+ if (validator === undefined) {
226
+ throw new Error("target has no attached validator.");
227
+ }
228
+ tasks.push(validator.validate());
229
+ }
230
+ return !(await Promise.all(tasks)).includes(false);
231
+ }
232
+
233
+ export function ValidationMessage(props: {
234
+ visible?: Expression<boolean | undefined>;
235
+ alert?: Event<[]>;
236
+ class?: ClassValue;
237
+ style?: StyleValue;
238
+ id?: Expression<string | undefined>;
239
+ children?: unknown;
240
+ }): unknown {
241
+ const theme = extract(THEME);
242
+ return <Collapse
243
+ visible={map(props.visible, v => v ?? true)}
244
+ alert={props.alert}
245
+ class={[
246
+ props.class,
247
+ theme?.validation_message_container,
248
+ ]}
249
+ style={props.style}
250
+ id={props.id}
251
+ >
252
+ <div class={theme?.validation_message}>
253
+ <Text>
254
+ {props.children}
255
+ </Text>
256
+ </div>
257
+ </Collapse>;
258
+ }
259
+
260
+ /**
261
+ * Render validation messages for the validator attached to a specific target.
262
+ */
263
+ export function ValidationMessages(props: {
264
+ for: object;
265
+ }): unknown {
266
+ const validator = Validator.attach(props.for);
267
+ return <For each={validator.rules}>
268
+ {rule => <ValidationMessage visible={rule.visible} alert={rule.alert.event} id={rule.id}>
269
+ {rule.message}
270
+ </ValidationMessage>}
271
+ </For>;
272
+ }
@@ -0,0 +1,22 @@
1
+ import { ClassValue, Expression, extract, StyleValue } from "rvx";
2
+
3
+ import { THEME } from "../common/theme.js";
4
+
5
+ export function Value(props: {
6
+ class?: ClassValue;
7
+ style?: StyleValue;
8
+ id?: Expression<string | undefined>;
9
+ children?: unknown;
10
+ }): unknown {
11
+ const theme = extract(THEME);
12
+ return <span
13
+ class={[
14
+ theme?.value,
15
+ props.class,
16
+ ]}
17
+ style={props.style}
18
+ id={props.id}
19
+ >
20
+ {props.children}
21
+ </span>;
22
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,29 @@
1
+ export * from "./common/events.js";
2
+ export * from "./common/parsers.js";
3
+ export * from "./common/theme.js";
4
+ export * from "./common/trim.js";
5
+ export * from "./common/types.js";
6
+ export * from "./common/writing-mode.js";
7
+ export * from "./components/button.js";
8
+ export * from "./components/checkbox.js";
9
+ export * from "./components/collapse.js";
10
+ export * from "./components/column.js";
11
+ export * from "./components/control-group.js";
12
+ export * from "./components/dialog.js";
13
+ export * from "./components/dropdown.js";
14
+ export * from "./components/dropdown-input.js";
15
+ export * from "./components/flex-space.js";
16
+ export * from "./components/heading.js";
17
+ export * from "./components/label.js";
18
+ export * from "./components/layer.js";
19
+ export * from "./components/link.js";
20
+ export * from "./components/page.js";
21
+ export * from "./components/popout.js";
22
+ export * from "./components/popover.js";
23
+ export * from "./components/radio-buttons.js";
24
+ export * from "./components/row.js";
25
+ export * from "./components/scroll-view.js";
26
+ export * from "./components/text.js";
27
+ export * from "./components/text-input.js";
28
+ export * from "./components/validation.js";
29
+ export * from "./components/value.js";
package/src/test.tsx ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./common/theme-test.js";
2
+ export * from "./components/collapse-test.js";
@@ -0,0 +1,69 @@
1
+ @use "sass:math";
2
+ @use "common";
3
+
4
+ $root-size: 14px;
5
+
6
+ @function px($value) {
7
+ @return math.div($value * 1px, $root-size) * 1rem;
8
+ }
9
+
10
+ :root {
11
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
12
+ font-size: #{math.div(1rem, 16px) * $root-size};
13
+ line-height: 1;
14
+
15
+ --content-column-gap: #{px(16)};
16
+ --content-row-gap: #{px(16)};
17
+ --content-radius: #{px(8)};
18
+ --content-border: #{px(2)};
19
+ @include common.define-quad(content-pad, px(20), px(20));
20
+
21
+ --control-column-gap: #{px(8)};
22
+ --control-row-gap: #{px(8)};
23
+ --control-radius: #{px(4)};
24
+ --control-border: #{px(1)};
25
+ --control-disabled: opacity(.5);
26
+ @include common.define-quad(control-pad, px(8), px(10));
27
+
28
+ --focus-outline-offset: #{px(2)};
29
+
30
+ --overflow-safe-area: #{px(4)};
31
+
32
+ --line-gap: #{px(6)};
33
+ --space-gap: #{px(6)};
34
+
35
+ --layout-transition: .15s ease;
36
+ }
37
+
38
+ @include common.theme((
39
+ bg: (
40
+ dark: rgb(32, 32, 32),
41
+ ),
42
+ fg: (
43
+ dark: white,
44
+ ),
45
+ focus-outline: (
46
+ dark: var(--control-border) dashed rgb(0, 127, 255),
47
+ ),
48
+ ));
49
+
50
+ body {
51
+ background-color: var(--bg);
52
+ color: var(--fg);
53
+
54
+ text-wrap: balance;
55
+ @supports (text-wrap: pretty) {
56
+ text-wrap: pretty;
57
+ }
58
+ }
59
+
60
+ * {
61
+ box-sizing: border-box;
62
+ --space-scale: 1;
63
+ --space-above: unset;
64
+ --space-below: unset;
65
+ }
66
+
67
+ ::selection {
68
+ background-color: rgba(0, 100, 200, .7);
69
+ }
@@ -0,0 +1,51 @@
1
+ @use "sass:map";
2
+
3
+ @mixin set-theme-vars($defs, $target) {
4
+ @each $name, $targets in $defs {
5
+ @if map.has-key($targets, $target) {
6
+ --#{$name}: #{map.get($targets, $target)};
7
+ }
8
+ }
9
+ }
10
+
11
+ @mixin theme($defs) {
12
+ :root {
13
+ @include set-theme-vars($defs, light);
14
+ @media (prefers-color-scheme: dark) {
15
+ @include set-theme-vars($defs, dark);
16
+ color-scheme: dark;
17
+ }
18
+
19
+ @media (prefers-contrast: more) {
20
+ @include set-theme-vars($defs, light-hc);
21
+ @media (prefers-color-scheme: dark) {
22
+ @include set-theme-vars($defs, dark-hc);
23
+ }
24
+ }
25
+ }
26
+ }
27
+
28
+ @mixin define-quad($name, $block-start, $inline-start: $block-start, $block-end: $block-start, $inline-end: $inline-start) {
29
+ --#{$name}-block-start: #{$block-start};
30
+ --#{$name}-inline-start: #{$inline-start};
31
+ --#{$name}-block-end: #{$block-end};
32
+ --#{$name}-inline-end: #{$inline-end};
33
+ }
34
+
35
+ @mixin padding($name, $subtract: null) {
36
+ @if $subtract {
37
+ padding-block:
38
+ calc(var(--#{$name}-block-start) - $subtract)
39
+ calc(var(--#{$name}-block-end) - $subtract);
40
+ padding-inline:
41
+ calc(var(--#{$name}-inline-start) - $subtract)
42
+ calc(var(--#{$name}-inline-end) - $subtract);
43
+ } @else {
44
+ padding-block:
45
+ var(--#{$name}-block-start)
46
+ var(--#{$name}-block-end);
47
+ padding-inline:
48
+ var(--#{$name}-inline-start)
49
+ var(--#{$name}-inline-end);
50
+ }
51
+ }
@@ -0,0 +1,116 @@
1
+ @use "../common";
2
+
3
+ @include common.theme((
4
+ button-default-bg: (
5
+ dark: rgb(54, 54, 54),
6
+ ),
7
+ button-default-bg-lit: (
8
+ dark: rgb(72, 72, 72),
9
+ ),
10
+ button-default-fg: (
11
+ dark: var(--fg),
12
+ ),
13
+ button-default-border: (
14
+ dark: rgb(80, 80, 80),
15
+ ),
16
+
17
+ button-primary-bg: (
18
+ dark: rgb(0, 100, 200),
19
+ ),
20
+ button-primary-bg-lit: (
21
+ dark: rgb(0, 128, 255),
22
+ ),
23
+ button-primary-fg: (
24
+ dark: white,
25
+ ),
26
+ button-primary-border: (
27
+ dark: rgb(0, 100, 200),
28
+ ),
29
+
30
+ button-success-bg: (
31
+ dark: rgb(0, 200, 100),
32
+ ),
33
+ button-success-bg-lit: (
34
+ dark: rgb(0, 242, 121),
35
+ ),
36
+ button-success-fg: (
37
+ dark: black,
38
+ ),
39
+ button-success-border: (
40
+ dark: rgb(0, 200, 100),
41
+ ),
42
+
43
+ button-warning-bg: (
44
+ dark: rgb(255, 200, 0),
45
+ ),
46
+ button-warning-bg-lit: (
47
+ dark: rgb(255, 224, 122),
48
+ ),
49
+ button-warning-fg: (
50
+ dark: black,
51
+ ),
52
+ button-warning-border: (
53
+ dark: rgb(255, 200, 0),
54
+ ),
55
+
56
+ button-danger-bg: (
57
+ dark: rgb(255, 0, 64),
58
+ ),
59
+ button-danger-bg-lit: (
60
+ dark: rgb(255, 64, 96),
61
+ ),
62
+ button-danger-fg: (
63
+ dark: black,
64
+ ),
65
+ button-danger-border: (
66
+ dark: rgb(255, 0, 64),
67
+ ),
68
+
69
+ button-input-bg: (
70
+ dark: rgb(16, 16, 16),
71
+ ),
72
+ button-input-bg-lit: (
73
+ dark: rgb(36, 36, 36),
74
+ ),
75
+ button-input-fg: (
76
+ dark: white,
77
+ ),
78
+ button-input-border: (
79
+ dark: rgb(80, 80, 80),
80
+ ),
81
+ ));
82
+
83
+ .button {
84
+ font-family: inherit;
85
+ font-size: inherit;
86
+ font-weight: 600;
87
+ line-height: 1;
88
+
89
+ cursor: pointer;
90
+
91
+ outline: none;
92
+ @include common.padding(control-pad, var(--control-border));
93
+
94
+ border-radius: var(--control-radius);
95
+
96
+ &[disabled] {
97
+ cursor: default;
98
+ filter: var(--control-disabled);
99
+ }
100
+ }
101
+
102
+ @each $variant in (default, primary, success, warning, danger, input) {
103
+ .button_#{$variant} {
104
+ background-color: var(--button-#{$variant}-bg);
105
+ color: var(--button-#{$variant}-fg);
106
+ border: var(--control-border) solid var(--button-#{$variant}-border, transparent);
107
+ &:hover:not(:active):not([disabled]),
108
+ &:focus-visible:not([disabled]) {
109
+ background-color: var(--button-#{$variant}-bg-lit);
110
+ }
111
+ }
112
+ }
113
+
114
+ .button_input {
115
+ text-align: left;
116
+ }
@@ -0,0 +1,25 @@
1
+
2
+ .checkbox_label {
3
+ display: flex;
4
+ column-gap: var(--control-column-gap);
5
+ align-items: start;
6
+
7
+ &:has(.checkbox_input:not(:disabled)) {
8
+ cursor: pointer;
9
+ }
10
+
11
+ &:has(.checkbox_input:disabled) > .checkbox_content {
12
+ filter: var(--control-disabled);
13
+ }
14
+
15
+ &:has(.checkbox_input:focus-visible) {
16
+ outline: var(--focus-outline);
17
+ outline-offset: var(--focus-outline-offset);
18
+ }
19
+ }
20
+
21
+ .checkbox_input {
22
+ margin: 0;
23
+ outline: none;
24
+ cursor: inherit;
25
+ }
@@ -0,0 +1,64 @@
1
+
2
+ .collapse {
3
+ display: block;
4
+ position: relative;
5
+ --space-scale: 0;
6
+ }
7
+
8
+ .collapse_sized {
9
+ transition:
10
+ margin-block-start var(--layout-transition),
11
+ margin-block-end var(--layout-transition);
12
+ }
13
+
14
+ @keyframes alert {
15
+ from { transform: translateX(0rem); }
16
+ 30% { transform: translateX(.2rem); }
17
+ 70% { transform: translateX(-.2rem); }
18
+ to { transform: translateX(0rem); }
19
+ }
20
+
21
+ .collapse_alert {
22
+ animation: alert .2s ease;
23
+ }
24
+
25
+ .collapse_visible.collapse_visible {
26
+ --space-scale: 1;
27
+ }
28
+
29
+ .collapse_view {
30
+ block-size: 0px;
31
+ position: relative;
32
+ overflow: hidden;
33
+
34
+ .collapse_sized > & {
35
+ transition:
36
+ block-size var(--layout-transition),
37
+ margin var(--layout-transition);
38
+ }
39
+
40
+ .collapse_visible > & {
41
+ block-size: calc(var(--collapse-size) + var(--overflow-safe-area) * 2);
42
+ margin: calc(var(--overflow-safe-area) * -1);
43
+ }
44
+ }
45
+
46
+ .collapse_content {
47
+ position: absolute;
48
+ inline-size: 100%;
49
+ top: 0px;
50
+ left: 0px;
51
+
52
+ .collapse_sized > * > & {
53
+ transition:
54
+ top var(--layout-transition),
55
+ left var(--layout-transition),
56
+ inline-size var(--layout-transition);
57
+ }
58
+
59
+ .collapse_visible & {
60
+ top: var(--overflow-safe-area);
61
+ left: var(--overflow-safe-area);
62
+ inline-size: calc(100% - var(--overflow-safe-area) * 2);
63
+ }
64
+ }
@@ -0,0 +1,28 @@
1
+
2
+ .column {
3
+ display: flex;
4
+ flex-direction: column;
5
+
6
+ & > :not(:first-child) {
7
+ margin-block-start: calc(
8
+ (var(--space-above, var(--parent-row-gap)) - var(--parent-row-gap)) * var(--space-scale)
9
+ + var(--parent-row-gap) * (var(--space-scale) - 1) / 2
10
+ );
11
+ }
12
+
13
+ & > :not(:last-child) {
14
+ margin-block-end: calc(
15
+ (var(--space-below, var(--parent-row-gap)) - var(--parent-row-gap)) * var(--space-scale)
16
+ + var(--parent-row-gap) * (var(--space-scale) - 1) / 2
17
+ );
18
+ }
19
+ }
20
+
21
+ @each $size in "content", "control" {
22
+ .column_#{$size} {
23
+ row-gap: var(--#{$size}-row-gap);
24
+ & > * {
25
+ --parent-row-gap: var(--#{$size}-row-gap);
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,14 @@
1
+
2
+ .control_group {
3
+ display: flex;
4
+ flex-direction: row;
5
+ flex-wrap: wrap;
6
+ & > :not(:first-child) {
7
+ border-start-start-radius: 0;
8
+ border-end-start-radius: 0;
9
+ }
10
+ & > :not(:last-child) {
11
+ border-start-end-radius: 0;
12
+ border-end-end-radius: 0;
13
+ }
14
+ }