Skip to content

Commit 9190ffa

Browse files
Merge pull request #108 from gridaco/support-button
Support web buttons
2 parents f943553 + da55d39 commit 9190ffa

25 files changed

Lines changed: 560 additions & 99 deletions

File tree

editor/components/app-runner/vanilla-app-runner.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export function VanillaRunner({
4343
if (ref.current && enableInspector) {
4444
ref.current.onload = () => {
4545
const matches = ref.current.contentDocument.querySelectorAll(
46-
"div, span, button, img, image, svg"
46+
"div, span, img, image, svg" // button, input - disabled due to interaction testing (for users)
4747
);
4848
matches.forEach((el) => {
4949
const tint = "rgba(20, 0, 255, 0.2)";

externals/reflect-core

packages/builder-css-styles/padding/index.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ import { px } from "../dimensions";
88

99
type PaddingValue = number | "auto";
1010

11-
export function padding(p: EdgeInsets): CSSProperties {
12-
switch (edgeInsetsShorthandMode(p)) {
11+
export function padding(
12+
p: EdgeInsets,
13+
options?: {
14+
explicit?: boolean;
15+
}
16+
): CSSProperties {
17+
switch (edgeInsetsShorthandMode(p, options)) {
1318
case EdgeInsetsShorthandMode.empty: {
1419
return {};
1520
}
@@ -31,10 +36,10 @@ export function padding(p: EdgeInsets): CSSProperties {
3136
case EdgeInsetsShorthandMode.trbl:
3237
default: {
3338
return {
34-
"padding-bottom": _makeifRequired(p?.bottom),
35-
"padding-top": _makeifRequired(p?.top),
36-
"padding-left": _makeifRequired(p?.left),
37-
"padding-right": _makeifRequired(p?.right),
39+
"padding-bottom": _makeifRequired(p?.bottom, options?.explicit),
40+
"padding-top": _makeifRequired(p?.top, options?.explicit),
41+
"padding-left": _makeifRequired(p?.left, options?.explicit),
42+
"padding-right": _makeifRequired(p?.right, options?.explicit),
3843
};
3944
}
4045
}
@@ -55,8 +60,8 @@ function _pv(pv: PaddingValue) {
5560
return px(pv);
5661
}
5762

58-
function _makeifRequired(val: number): string | undefined {
59-
if (val && val > 0) {
63+
function _makeifRequired(val: number, explicit?: boolean): string | undefined {
64+
if (explicit || (val && val > 0)) {
6065
return px(val);
6166
}
6267
}

packages/builder-css-styles/text-shadow/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { color } from "../color";
33
import { px } from "../dimensions";
44

55
export function textShadow(ts: TextShadowManifest[]): string {
6-
if (ts.length === 0) {
6+
if (!ts || ts.length === 0) {
77
return;
88
}
99

packages/builder-web-core/widget-core/widget-with-style.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { JsxWidget, IMultiChildJsxWidget, JSXElementConfig } from ".";
2-
import { CSSProperties } from "@coli.codes/css";
2+
import { ElementCssProperties, ElementCssStyleData } from "@coli.codes/css";
33
import {
44
Color,
55
DimensionLength,
@@ -15,13 +15,13 @@ import { WidgetKey } from "../widget-key";
1515
import { positionAbsolute } from "@web-builder/styles";
1616

1717
export interface IWidgetWithStyle {
18-
styleData(): CSSProperties;
18+
styleData(): ElementCssStyleData;
1919
}
2020

2121
/**
2222
* Since html based framework's widget can be represented withou any style definition, this WidgetWithStyle class indicates, that the sub instance of this class will contain style data within it.
2323
*/
24-
export abstract class WidgetWithStyle<OUTSTYLE = CSSProperties>
24+
export abstract class WidgetWithStyle<OUTSTYLE = ElementCssStyleData>
2525
extends JsxWidget
2626
implements
2727
IWHStyleWidget,
@@ -81,8 +81,8 @@ export abstract class WidgetWithStyle<OUTSTYLE = CSSProperties>
8181

8282
abstract jsxConfig(): JSXElementConfig;
8383

84-
private extendedStyle: CSSProperties = {};
85-
extendStyle<T = CSSProperties>(style: T) {
84+
private extendedStyle: ElementCssProperties = {};
85+
extendStyle<T = ElementCssProperties>(style: T) {
8686
this.extendedStyle = {
8787
...this.extendedStyle,
8888
...style,
@@ -102,7 +102,7 @@ export abstract class MultiChildWidgetWithStyle
102102
constructor({ key }: { key: WidgetKey }) {
103103
super({ key: key });
104104
}
105-
abstract styleData(): CSSProperties;
105+
abstract styleData(): ElementCssStyleData;
106106

107107
abstract jsxConfig(): JSXElementConfig;
108108
}
Lines changed: 193 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,207 @@
1-
import { JSXElementConfig, WidgetKey } from "@web-builder/core";
2-
import { JSX, JSXAttribute, Snippet } from "coli";
3-
import { ReactWidget } from "..";
1+
import type { CSSProperties, ElementCssStyleData } from "@coli.codes/css";
2+
import type { JSXElementConfig, StylableJsxWidget } from "@web-builder/core";
3+
import type {
4+
IButtonStyleButton,
5+
IButtonStyleButtonProps,
6+
ITextStyle,
7+
ButtonStyle,
8+
IWHStyleWidget,
9+
Widget,
10+
} from "@reflect-ui/core";
11+
import { Text } from "@reflect-ui/core";
12+
import { Container } from "..";
13+
import { WidgetKey } from "../../widget-key";
14+
import { JSX } from "coli";
15+
import * as css from "@web-builder/styles";
416

5-
export class Button extends ReactWidget {
6-
constructor({ key }: { key: WidgetKey }) {
7-
super({ key });
8-
}
17+
/**
18+
* Html5 Button Will have built in support for...
19+
*
20+
*
21+
* - onClick callback
22+
* - hover styles
23+
* - focus styles
24+
* - disabled styles
25+
* - active styles
26+
*
27+
*
28+
* Learn more at
29+
* - [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)
30+
* - [html spec](https://html.spec.whatwg.org/multipage/form-elements.html#the-button-element)
31+
*
32+
*/
33+
export class HtmlButton extends Container implements IButtonStyleButton {
34+
_type = "button";
35+
36+
/**
37+
* The name of the button, submitted as a pair with the button’s value as part of the form data, when that button is used to submit the form.
38+
*/
39+
name?: string;
40+
41+
/**
42+
* The default behavior of the button. Possible values are:
43+
* - `submit`: The button submits the form data to the server. This is the default if the attribute is not specified for buttons associated with a <form>, or if the attribute is an empty or invalid value.
44+
* - `reset`: The button resets all the controls to their initial values, like <input type="reset">. (This behavior tends to annoy users.)
45+
* - `button`: The button has no default behavior, and does nothing when pressed by default. It can have client-side scripts listen to the element's events, which are triggered when the events occur.
46+
*/
47+
type: "submit" | "reset" | "button" = "button";
48+
49+
// #region @ButtonStyleButton
50+
autofocus?: boolean;
51+
style: ButtonStyle;
52+
/**
53+
* This Boolean attribute prevents the user from interacting with the button: it cannot be pressed or focused.
54+
*/
55+
disabled?: boolean;
56+
// TextManifest
57+
child: Widget;
58+
// #endregion @ButtonStyleButton
59+
60+
constructor({
61+
key,
62+
name,
63+
autofocus,
64+
disabled,
65+
style,
66+
child,
67+
...rest
68+
}: { key: WidgetKey } & {
69+
name?: string;
70+
} & IButtonStyleButtonProps &
71+
IWHStyleWidget) {
72+
super({ key, ...rest });
73+
74+
// set button properties
75+
this.name = name;
76+
77+
this.autofocus = autofocus;
78+
this.disabled = disabled;
79+
this.style = style;
80+
this.child = child;
981

10-
children = [
1182
//
12-
];
83+
this.children = this.makechildren();
84+
}
85+
86+
makechildren() {
87+
if (this.child instanceof Text) {
88+
return [
89+
<StylableJsxWidget>{
90+
key: new WidgetKey(`${this.key.id}.text`, "text"),
91+
styleData: () => null,
92+
jsxConfig: () => {
93+
return <JSXElementConfig>{
94+
type: "static-tree",
95+
tree: JSX.text((this.child as Text).data as string),
96+
};
97+
},
98+
},
99+
];
100+
}
101+
102+
return [];
103+
}
104+
105+
styleData(): ElementCssStyleData {
106+
const containerstyle = super.styleData();
107+
108+
// wip
109+
return {
110+
// general layouts, continer ---------------------
111+
...containerstyle,
112+
// -----------------------------------------------
113+
114+
// padding
115+
...css.padding(this.style.padding?.default),
116+
"box-sizing": (this.padding && "border-box") || undefined,
13117

14-
styleData() {
15-
return {};
118+
// background
119+
"background-color": this.style.backgroundColor
120+
? css.color(this.style.backgroundColor.default)
121+
: undefined,
122+
123+
// text styles --------------------------------------------
124+
color: css.color((this.style.textStyle.default as ITextStyle)?.color),
125+
// "text-overflow": this.overflow,
126+
"font-size": css.px(this.style.textStyle.default.fontSize),
127+
"font-family": css.fontFamily(this.style.textStyle.default.fontFamily),
128+
"font-weight": css.convertToCssFontWeight(
129+
this.style.textStyle.default.fontWeight
130+
),
131+
// "word-spacing": this.style.wordSpacing,
132+
"letter-spacing": css.letterSpacing(
133+
this.style.textStyle.default.letterSpacing
134+
),
135+
"line-height": css.length(this.style.textStyle.default.lineHeight),
136+
// "text-align": this.textAlign,
137+
"text-decoration": css.textDecoration(
138+
this.style.textStyle.default.decoration
139+
),
140+
"text-shadow": css.textShadow(this.style.textStyle.default.textShadow),
141+
"text-transform": css.textTransform(
142+
this.style.textStyle.default.textTransform
143+
),
144+
// text styles --------------------------------------------
145+
146+
//
147+
width: undefined, // clear fixed width
148+
"min-width": css.length(this.minWidth),
149+
"min-height": css.length(this.minHeight),
150+
151+
border: containerstyle["border"] ?? "none",
152+
outline: containerstyle["outline"] ?? "none",
153+
154+
// button cursor
155+
cursor: "pointer",
156+
157+
":hover": _button_hover_style,
158+
":disabled": _button_disabled_style,
159+
":active": _button_active_style,
160+
":focus": _button_focus_style,
161+
};
16162
}
17163

164+
// @ts-ignore
18165
jsxConfig() {
19166
return <JSXElementConfig>{
20167
tag: JSX.identifier("button"),
21168
attributes: [
22-
new JSXAttribute(
23-
"onClick",
24-
Snippet.fromStatic("() => { alert(`button click`) }")
25-
),
169+
// wip
170+
// TODO: this only works for React. (wont' work for vanilla html)
171+
// new JSXAttribute(
172+
// "onClick",
173+
// JSX.exp(
174+
// // Snippet.fromStatic()
175+
// // TODO: type check
176+
// "() => { /* add onclick callback */ }" as any
177+
// )
178+
// ),
26179
],
27180
};
28181
}
182+
183+
get finalStyle() {
184+
const superstyl = super.finalStyle;
185+
186+
// width override. ------------------------------------------------------------------------------------------
187+
return {
188+
...superstyl,
189+
width: undefined,
190+
};
191+
// ----------------------------------------------------------------------------------------------------------
192+
}
29193
}
194+
195+
const _button_hover_style: CSSProperties = {
196+
opacity: 0.8,
197+
};
198+
199+
const _button_disabled_style: CSSProperties = {
200+
opacity: 0.5,
201+
};
202+
203+
const _button_active_style: CSSProperties = {
204+
opacity: 1,
205+
};
206+
207+
const _button_focus_style: CSSProperties = {};

packages/builder-web-core/widgets-native/html-input/html-input-text.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CSSProperties } from "@coli.codes/css";
1+
import { CSSProperties, ElementCssStyleData } from "@coli.codes/css";
22
import { StylableJSXElementConfig, WidgetKey } from "../..";
33
import * as css from "@web-builder/styles";
44
import { JSX, JSXAttribute, StringLiteral } from "coli";
@@ -113,7 +113,7 @@ export class HtmlInputText extends Container implements ITextFieldManifest {
113113
this.padding = this.decoration.contentPadding;
114114
}
115115

116-
styleData(): CSSProperties {
116+
styleData(): ElementCssStyleData {
117117
// TODO:
118118
// - support placeholder text color styling
119119
const containerstyle = super.styleData();
@@ -153,6 +153,19 @@ export class HtmlInputText extends Container implements ITextFieldManifest {
153153
"text-shadow": css.textShadow(this.style.textShadow),
154154
"text-transform": css.textTransform(this.style.textTransform),
155155
// text styles --------------------------------------------
156+
157+
...(this.decoration?.placeholderStyle
158+
? {
159+
"::placeholder": {
160+
// TODO: optmiize - assign only diffferent properties values
161+
// TODO: not all properties are assigned
162+
color: css.color(this.decoration.placeholderStyle.color),
163+
"font-size": css.px(this.style.fontSize),
164+
"font-family": css.fontFamily(this.style.fontFamily),
165+
"font-weight": css.convertToCssFontWeight(this.style.fontWeight),
166+
},
167+
}
168+
: {}),
156169
};
157170
}
158171

packages/builder-web-core/widgets-native/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from "./stack";
66
export * from "./html-text-element";
77
export * from "./html-svg";
88
export * from "./html-image";
9+
export * from "./html-button";
910
export * from "./html-input";
1011
export * from "./error-widget";
1112

0 commit comments

Comments
 (0)