Skip to content

Commit a0dd9fd

Browse files
add checkbox flag & mapping
1 parent 9eec1cc commit a0dd9fd

File tree

12 files changed

+282
-4
lines changed

12 files changed

+282
-4
lines changed

externals/reflect-core

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import type { ElementCssStyleData } from "@coli.codes/css";
2+
import type {
3+
BorderSide,
4+
Color,
5+
ICheckboxManifest,
6+
IWHStyleWidget,
7+
MouseCursor,
8+
} from "@reflect-ui/core";
9+
import { WidgetKey } from "../../widget-key";
10+
import type { StylableJSXElementConfig } from "../../widget-core";
11+
import { Container } from "../container";
12+
import * as css from "@web-builder/styles";
13+
import { JSX, JSXAttribute, StringLiteral } from "coli";
14+
15+
/**
16+
* A jsx attibute to indicate input type as checkbox
17+
*/
18+
const attr_type_range = new JSXAttribute("type", new StringLiteral("checkbox"));
19+
20+
/**
21+
* checkbox
22+
*
23+
*
24+
* @see
25+
* - [html#progress](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)
26+
*/
27+
export class HtmlInputCheckbox extends Container implements ICheckboxManifest {
28+
_type = "input/checkbox";
29+
30+
value?: boolean;
31+
32+
/**
33+
* @deprecated - not supported natively
34+
*/
35+
tristate?: boolean;
36+
mouseCursor?: MouseCursor;
37+
activeColor?: Color;
38+
// fillColor?: ReflectStateProperty<Color>;
39+
checkColor?: Color;
40+
focusColor?: Color;
41+
42+
/**
43+
* @deprecated - not supported natively
44+
*/
45+
hoverColor?: Color;
46+
// overlayColor?: ReflectStateProperty<Color>;
47+
48+
/**
49+
* @deprecated - not supported natively
50+
*/
51+
splashRadius?: number;
52+
// visualDensity?: VisualDensity,
53+
autofocus?: boolean;
54+
// shape?: OutlinedBorder;
55+
56+
/**
57+
* @deprecated - not supported natively
58+
*/
59+
side?: BorderSide;
60+
61+
constructor({
62+
key,
63+
64+
value,
65+
tristate,
66+
mouseCursor,
67+
activeColor,
68+
checkColor,
69+
focusColor,
70+
hoverColor,
71+
splashRadius,
72+
autofocus,
73+
side,
74+
75+
...rest
76+
}: {
77+
key: WidgetKey;
78+
} & IWHStyleWidget &
79+
ICheckboxManifest) {
80+
super({ key, ...rest });
81+
82+
this.value = value;
83+
this.tristate = tristate;
84+
this.mouseCursor = mouseCursor;
85+
this.activeColor = activeColor;
86+
this.checkColor = checkColor;
87+
this.focusColor = focusColor;
88+
this.hoverColor = hoverColor;
89+
this.splashRadius = splashRadius;
90+
this.autofocus = autofocus;
91+
this.side = side;
92+
}
93+
94+
get indeterminate() {
95+
return this.value === undefined;
96+
}
97+
98+
styleData(): ElementCssStyleData {
99+
const containerstyle = super.styleData();
100+
101+
return {
102+
// general layouts, continer ---------------------
103+
...containerstyle,
104+
// -------------------------------------------------
105+
106+
"border-radius": undefined, // clear to use default appearance
107+
"background-color": undefined, // clear to use default appearance
108+
padding: undefined,
109+
110+
// general slider styles
111+
"accent-color": css.color(this.activeColor),
112+
};
113+
}
114+
115+
jsxConfig(): StylableJSXElementConfig {
116+
const attrs = [
117+
attr_type_range,
118+
this.indeterminate
119+
? new JSXAttribute("indeterminate")
120+
: new JSXAttribute(
121+
"value",
122+
new StringLiteral(this.value ? "checked" : "unchecked")
123+
),
124+
].filter(Boolean);
125+
126+
return <StylableJSXElementConfig>{
127+
type: "tag-and-attr",
128+
tag: JSX.identifier("input"),
129+
attributes: attrs,
130+
};
131+
}
132+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { HtmlTextField as TextInput } from "./html-input-text";
22
export { HtmlSlider as Slider } from "./html-input-range";
3+
export { HtmlInputCheckbox as Checkbox } from "./html-input-checkbox";

packages/designto-token/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ function handleNode(
183183
if (config.disable_detection) {
184184
// skip detection
185185
} else {
186+
// TODO: only execute detection if all the nested children is not flagged as other component.
187+
186188
// - icon -
187189
const _detect_if_icon = detectIf.icon(node);
188190
if (_detect_if_icon.result) {

packages/designto-token/support-flags/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { tokenize_flagged_progress } from "./token-progress";
2121
import { tokenize_flagged_google_maps_view } from "./token-x-google-maps-view";
2222
import { tokenize_flagged_youtube_view } from "./token-x-youtube-view";
2323
import { tokenize_flagged_camera_view } from "./token-x-camera-display";
24+
import { tokenize_flagged_checkbox } from "./token-checkbox";
2425

2526
export default function handleWithFlags(node: ReflectSceneNode) {
2627
const flags = parse(node.name);
@@ -54,6 +55,10 @@ function _handle_with_flags(node, flags: FlagsParseResult) {
5455
return tokenize_flagged_button(node, flags[keys.flag_key__as_button]);
5556
}
5657

58+
if (flags.__meta?.contains_checkbox_flag) {
59+
return tokenize_flagged_checkbox(node, flags[keys.flag_key__as_checkbox]);
60+
}
61+
5762
if (flags.__meta?.contains_input_flag) {
5863
return tokenize_flagged_textfield(node, flags[keys.flag_key__as_input]);
5964
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Checkbox, Container, ICheckboxManifest } from "@reflect-ui/core";
2+
import type { AsCheckboxFlag } from "@code-features/flags";
3+
import type {
4+
ReflectFrameNode,
5+
ReflectSceneNode,
6+
} from "@design-sdk/figma-node";
7+
import { WrappingContainer } from "../../tokens";
8+
import assert from "assert";
9+
import { unwrappedChild } from "../../wrappings";
10+
import { tokenizeLayout } from "../../token-layout";
11+
import { keyFromNode } from "../../key";
12+
13+
export function tokenize_flagged_checkbox(
14+
node: ReflectSceneNode,
15+
flag: AsCheckboxFlag
16+
): Checkbox | WrappingContainer<Checkbox> {
17+
if (flag.value === false) return;
18+
19+
const validated = validate_checkbox(node);
20+
if (validated.error === false) {
21+
switch (validated.__type) {
22+
case "frame-as-checkbox": {
23+
const { checkbox_base, checkbox_value } = validated;
24+
25+
const _key = keyFromNode(node);
26+
27+
const container = unwrappedChild(
28+
tokenizeLayout.fromFrame(
29+
checkbox_base,
30+
checkbox_base.children,
31+
{ is_root: false },
32+
{}
33+
)
34+
) as Container;
35+
36+
const checked = checkbox_value && checkbox_value.visible;
37+
38+
return new WrappingContainer({
39+
...container,
40+
key: keyFromNode(node),
41+
child: new Checkbox({
42+
key: _key,
43+
fillColor: { default: checkbox_base?.primaryColor },
44+
checkColor: checkbox_value?.primaryColor,
45+
value: checked,
46+
}),
47+
});
48+
}
49+
default:
50+
throw new Error("unreachable");
51+
}
52+
} else {
53+
throw new Error(validated.error);
54+
}
55+
}
56+
57+
/**
58+
* validate if layer casted as checkbox can be actually tokenized to checkbox.
59+
* @param node
60+
*/
61+
function validate_checkbox(node: ReflectSceneNode):
62+
| {
63+
__type: "frame-as-checkbox";
64+
error: false;
65+
checkbox_base: ReflectFrameNode;
66+
checkbox_value?: ReflectSceneNode;
67+
}
68+
| { error: string } {
69+
assert(!!node, "target must not be null or undefined");
70+
switch (node.type) {
71+
case "FRAME": {
72+
const firstvaluenode = node.children.find(
73+
(child) => child.type === "VECTOR"
74+
);
75+
76+
return {
77+
__type: "frame-as-checkbox",
78+
checkbox_base: node,
79+
checkbox_value: firstvaluenode,
80+
error: false,
81+
};
82+
}
83+
default:
84+
return { error: "checkbox target is not a valid frame node" };
85+
}
86+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as web from "@web-builder/core";
2+
import * as core from "@reflect-ui/core";
3+
4+
export function compose_unwrapped_checkbox(
5+
key,
6+
widget: core.Checkbox,
7+
container?: core.Container
8+
): web.Checkbox {
9+
return new web.Checkbox({ ...(container ?? {}), ...widget, key });
10+
}

packages/designto-web/tokens-to-web-widget/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { compose_wrapped_with_overflow_box } from "./compose-wrapped-with-overfl
1717
import { compose_wrapped_with_expanded } from "./compose-wrapped-with-expanded";
1818
import { compose_unwrapped_text_input } from "./compose-unwrapped-text-field";
1919
import { compose_unwrapped_button } from "./compose-unwrapped-button";
20+
import { compose_unwrapped_checkbox } from "./compose-unwrapped-checkbox";
2021
import { compose_unwrapped_slider } from "./compose-unwrapped-slider";
2122
import { compose_unwrapped_progress } from "./compose-unwrapped-progress";
2223
import { compose_instanciation } from "./compose-instanciation";
@@ -242,6 +243,10 @@ function compose<T extends JsxWidget>(
242243
// TODO: widget.icon - not supported
243244
thisWebWidget = compose_unwrapped_button(_key, widget);
244245
}
246+
// checkbox
247+
else if (widget instanceof core.Checkbox) {
248+
thisWebWidget = compose_unwrapped_checkbox(_key, widget);
249+
}
245250
// textfield
246251
else if (widget instanceof core.TextField) {
247252
thisWebWidget = compose_unwrapped_text_input(_key, widget);
@@ -263,6 +268,8 @@ function compose<T extends JsxWidget>(
263268
thisWebWidget = compose_unwrapped_text_input(_key, widget.child, widget);
264269
} else if (widget.child instanceof core.ButtonStyleButton) {
265270
thisWebWidget = compose_unwrapped_button(_key, widget.child, widget);
271+
} else if (widget.child instanceof core.Checkbox) {
272+
thisWebWidget = compose_unwrapped_checkbox(_key, widget.child, widget);
266273
} else if (widget.child instanceof core.Slider) {
267274
thisWebWidget = compose_unwrapped_slider(_key, widget.child, widget);
268275
} else if (widget.child instanceof core.ProgressIndicator) {
@@ -283,6 +290,10 @@ function compose<T extends JsxWidget>(
283290
// --- --- --- --- ---
284291
// #endregion
285292
else {
293+
console.error(
294+
`Unsupported web widget type: ${widget.child.constructor.name}`,
295+
widget.child
296+
);
286297
throw new Error(
287298
`Unsupported web widget type: ${widget.child.constructor.name}`
288299
);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// primary
2+
export const flag_key__as_checkbox = "as-checkbox";
3+
4+
export const flag_key_alias__as_checkbox = [flag_key__as_checkbox];
5+
6+
export interface AsCheckboxFlag {
7+
flag: typeof flag_key__as_checkbox;
8+
9+
value: boolean;
10+
_raw?: string;
11+
}

packages/support-flags/keys.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { flag_key_alias__as_p, flag_key__as_p } from "./--as-p";
1010
import { flag_key_alias__as_span, flag_key__as_span } from "./--as-span";
1111

1212
import { flag_key__as_button, flag_key_alias__as_button } from "./--as-button";
13+
// prettier-ignore
14+
import { flag_key__as_checkbox, flag_key_alias__as_checkbox } from "./--as-checkbox";
1315
import { flag_key__as_input, flag_key_alias_as_input } from "./--as-input";
1416
import { flag_key__as_slider, flag_key_alias__as_slider } from "./--as-slider";
1517
// prettier-ignore
@@ -61,11 +63,9 @@ export { flag_key__as_p };
6163
export { flag_key__as_span };
6264

6365
export { flag_key__as_button };
64-
66+
export { flag_key__as_checkbox };
6567
export { flag_key__as_input };
66-
6768
export { flag_key__as_slider };
68-
6969
export { flag_key__as_progress };
7070

7171
export {
@@ -101,6 +101,7 @@ export const alias = {
101101
as_p: flag_key_alias__as_p,
102102
as_span: flag_key_alias__as_span,
103103
as_button: flag_key_alias__as_button,
104+
as_checkbox: flag_key_alias__as_checkbox,
104105
as_input: flag_key_alias_as_input,
105106
as_slider: flag_key_alias__as_slider,
106107
as_progress: flag_key_alias__as_progress,

0 commit comments

Comments
 (0)