rettangoli-ui 0.1.0-rc1 → 0.1.0-rc3
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.
- package/README.md +16 -27
- package/dist/rettangoli-iife-layout.min.js +360 -284
- package/dist/rettangoli-iife-ui.min.js +382 -548
- package/package.json +13 -5
- package/src/common/BaseElement.js +182 -0
- package/src/common.js +190 -0
- package/src/components/dialog/dialog.handlers.js +5 -0
- package/src/components/dialog/dialog.store.js +24 -0
- package/src/components/dialog/dialog.view.yaml +40 -0
- package/src/components/form/form.handlers.js +30 -0
- package/src/components/form/form.store.js +45 -0
- package/src/components/form/form.view.yaml +47 -0
- package/src/components/navbar/navbar.examples.yaml +86 -0
- package/src/components/navbar/navbar.handlers.js +10 -0
- package/src/components/navbar/navbar.store.js +46 -0
- package/src/components/navbar/navbar.view.yaml +74 -0
- package/src/components/pageOutline/pageOutline.handlers.js +69 -0
- package/src/components/pageOutline/pageOutline.store.js +40 -0
- package/src/components/pageOutline/pageOutline.view.yaml +34 -0
- package/src/components/popover/popover.handlers.js +17 -0
- package/src/components/popover/popover.store.js +37 -0
- package/src/components/popover/popover.view.yaml +50 -0
- package/src/components/select/select.handlers.js +15 -0
- package/src/components/select/select.store.js +25 -0
- package/src/components/select/select.view.yaml +38 -0
- package/src/components/sidebar/sidebar.handlers.js +36 -0
- package/src/components/sidebar/sidebar.store.js +125 -0
- package/src/components/sidebar/sidebar.view.yaml +186 -0
- package/src/entry-iife-layout.js +15 -0
- package/src/entry-iife-ui.js +18 -0
- package/src/index.js +17 -0
- package/src/lib/uhtml.js +9 -0
- package/src/primitives/button.js +263 -0
- package/src/primitives/image.js +234 -0
- package/src/primitives/input.js +208 -0
- package/src/primitives/svg.js +95 -0
- package/src/primitives/text.js +141 -0
- package/src/primitives/textarea.js +103 -0
- package/src/primitives/view.js +215 -0
- package/src/setup.js +16 -0
- package/src/styles/anchorStyles.js +13 -0
- package/src/styles/buttonMarginStyles.js +84 -0
- package/src/styles/cursorStyles.js +12 -0
- package/src/styles/flexChildStyles.js +43 -0
- package/src/styles/flexDirectionStyles.js +74 -0
- package/src/styles/marginStyles.js +13 -0
- package/src/styles/paddingSvgStyles.js +120 -0
- package/src/styles/scrollStyles.js +22 -0
- package/src/styles/textColorStyles.js +14 -0
- package/src/styles/textStyles.js +62 -0
- package/src/styles/viewStyles.js +119 -0
package/package.json
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
{
|
2
2
|
"name": "rettangoli-ui",
|
3
|
-
"version": "0.1.0-
|
3
|
+
"version": "0.1.0-rc3",
|
4
4
|
"description": "A UI component library for building web interfaces.",
|
5
5
|
"main": "dist/rettangoli-esm.min.js",
|
6
6
|
"type": "module",
|
7
7
|
"files": [
|
8
|
-
"dist"
|
8
|
+
"dist",
|
9
|
+
"src"
|
9
10
|
],
|
11
|
+
"exports": {
|
12
|
+
".": "./src/index.js"
|
13
|
+
},
|
14
|
+
"module": "./src/index.js",
|
10
15
|
"repository": {
|
11
16
|
"type": "git",
|
12
17
|
"url": "git+https://github.com/yuusoft-org/rettangoli.git"
|
@@ -17,12 +22,14 @@
|
|
17
22
|
},
|
18
23
|
"license": "MIT",
|
19
24
|
"scripts": {
|
20
|
-
"build": "
|
25
|
+
"build:dev": "bun run ../rettangoli-cli/cli.js fe build && bun run esbuild-dev.js",
|
26
|
+
"build": "bun run ../rettangoli-cli/cli.js fe build && bun run esbuild.js"
|
21
27
|
},
|
22
28
|
"devDependencies": {
|
23
29
|
"esbuild": "^0.20.0",
|
24
|
-
"
|
30
|
+
"looks-same": "^9.0.1",
|
25
31
|
"pixelmatch": "^6.0.0",
|
32
|
+
"playwright": "^1.46.0",
|
26
33
|
"pngjs": "^7.0.0"
|
27
34
|
},
|
28
35
|
"keywords": [
|
@@ -40,6 +47,7 @@
|
|
40
47
|
"commander": "^13.1.0",
|
41
48
|
"js-yaml": "^4.1.0",
|
42
49
|
"liquidjs": "^10.21.0",
|
43
|
-
"
|
50
|
+
"rettangoli-fe": "workspace:*",
|
51
|
+
"snabbdom": "^3.6.2"
|
44
52
|
}
|
45
53
|
}
|
@@ -0,0 +1,182 @@
|
|
1
|
+
/**
|
2
|
+
* Subscribes to all observables and returns a functionthat will unsubscribe
|
3
|
+
* from all observables when called
|
4
|
+
* @param {*} observables
|
5
|
+
* @returns
|
6
|
+
*/
|
7
|
+
const subscribeAll = (observables) => {
|
8
|
+
// Subscribe to all observables and store the subscription objects
|
9
|
+
const subscriptions = observables.map((observable) => observable.subscribe());
|
10
|
+
|
11
|
+
// Return a function that will unsubscribe from all observables when called
|
12
|
+
return () => {
|
13
|
+
for (const subscription of subscriptions) {
|
14
|
+
if (subscription && typeof subscription.unsubscribe === "function") {
|
15
|
+
subscription.unsubscribe();
|
16
|
+
}
|
17
|
+
}
|
18
|
+
};
|
19
|
+
};
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Creates a base web component element to be used in environments
|
23
|
+
* where the output is strings such as server side rendering environments
|
24
|
+
* @param {Object} param
|
25
|
+
* @param {Function} param.render - The render function to be used in the element. compliant with uhtml.render
|
26
|
+
* @returns
|
27
|
+
*/
|
28
|
+
export const createSsrBaseElement = ({ render }) => {
|
29
|
+
|
30
|
+
class CSSStyleSheet {
|
31
|
+
_css = "";
|
32
|
+
|
33
|
+
get css() {
|
34
|
+
return this._css;
|
35
|
+
}
|
36
|
+
|
37
|
+
replaceSync(css) {
|
38
|
+
this._css = css;
|
39
|
+
}
|
40
|
+
}
|
41
|
+
class SsrRenderTarget {
|
42
|
+
innerHTML = "";
|
43
|
+
adoptedStyleSheets = [];
|
44
|
+
write(html) {
|
45
|
+
this.innerHTML = html;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
class BaseBaseSsrElement {
|
49
|
+
renderTarget = new SsrRenderTarget();
|
50
|
+
|
51
|
+
constructor() {
|
52
|
+
const styleSheet = new CSSStyleSheet();
|
53
|
+
styleSheet.replaceSync(`:host { display: contents; }`);
|
54
|
+
this.renderTarget.adoptedStyleSheets = [styleSheet];
|
55
|
+
}
|
56
|
+
}
|
57
|
+
return class BaseSsrElement extends createGenericBaseElement({
|
58
|
+
BaseElement: BaseBaseSsrElement,
|
59
|
+
render,
|
60
|
+
skipOnMount: true,
|
61
|
+
}) {};
|
62
|
+
};
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Creates a base web component element to be used in web browser environments
|
66
|
+
* @param {Object} param
|
67
|
+
* @param {Function} param.render - The render function to be used in the element. compliant with uhtml.render
|
68
|
+
* @returns
|
69
|
+
*/
|
70
|
+
export const createWebComponentBaseElement = ({ render, styleSheet }) => {
|
71
|
+
let actualStyleSheet;
|
72
|
+
|
73
|
+
if (styleSheet) {
|
74
|
+
actualStyleSheet = styleSheet;
|
75
|
+
} else {
|
76
|
+
actualStyleSheet = new CSSStyleSheet();
|
77
|
+
actualStyleSheet.replaceSync(`:host { display: contents; }`);
|
78
|
+
}
|
79
|
+
|
80
|
+
return class BaseWebComponentElement extends createGenericBaseElement({
|
81
|
+
BaseElement: HTMLElement,
|
82
|
+
render,
|
83
|
+
skipOnMount: false,
|
84
|
+
}) {
|
85
|
+
static get observedAttributes() {
|
86
|
+
return ["key"];
|
87
|
+
}
|
88
|
+
|
89
|
+
constructor() {
|
90
|
+
super();
|
91
|
+
this.renderTarget = this.attachShadow({ mode: "closed" });
|
92
|
+
}
|
93
|
+
|
94
|
+
connectedCallback() {
|
95
|
+
this.renderTarget.adoptedStyleSheets = [actualStyleSheet];
|
96
|
+
this.baseOnMount();
|
97
|
+
}
|
98
|
+
|
99
|
+
disconnectedCallback() {
|
100
|
+
this.baseOnUnmount();
|
101
|
+
}
|
102
|
+
|
103
|
+
attributeChangedCallback() {
|
104
|
+
setTimeout(() => {
|
105
|
+
this.reRender();
|
106
|
+
}, 0);
|
107
|
+
}
|
108
|
+
};
|
109
|
+
};
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Creates a base web component element to be used in environments
|
113
|
+
* where the output is strings such as server side rendering environments
|
114
|
+
* @param {Object} param
|
115
|
+
* @param {Object} param.BaseElement - The base element to be used in the element.
|
116
|
+
* @param {Function} param.render - The render function to be used in the element. compliant with uhtml.render
|
117
|
+
* @param {Boolean} param.skipOnMount - Whether to skip the onMount callback. Default is false.
|
118
|
+
* @returns
|
119
|
+
*/
|
120
|
+
export const createGenericBaseElement = ({ BaseElement, render, skipOnMount = false }) => {
|
121
|
+
class GenericBaseElement extends BaseElement {
|
122
|
+
|
123
|
+
_renderKey = 0;
|
124
|
+
_unmountCallback;
|
125
|
+
// renderTarget;
|
126
|
+
disableFirstAutomatedRender = false;
|
127
|
+
|
128
|
+
get renderKey() {
|
129
|
+
return String(this._renderKey);
|
130
|
+
}
|
131
|
+
|
132
|
+
baseOnMount = () => {
|
133
|
+
if (!skipOnMount && this.onMount) {
|
134
|
+
this._unmountCallback = this.onMount();
|
135
|
+
}
|
136
|
+
|
137
|
+
if (!this.disableFirstAutomatedRender) {
|
138
|
+
this.reRender();
|
139
|
+
}
|
140
|
+
|
141
|
+
if (this.attachedObservables) {
|
142
|
+
if (Array.isArray(this.attachedObservables)) {
|
143
|
+
this.unsubscribe = subscribeAll(this.attachedObservables);
|
144
|
+
} else {
|
145
|
+
this.unsubscribe = subscribeAll(this.attachedObservables());
|
146
|
+
}
|
147
|
+
} else if (this.subscriptions) {
|
148
|
+
this.unsubscribe = subscribeAll(this.subscriptions);
|
149
|
+
}
|
150
|
+
};
|
151
|
+
|
152
|
+
baseOnUnmount = () => {
|
153
|
+
if (this._unmountCallback) {
|
154
|
+
this._unmountCallback();
|
155
|
+
}
|
156
|
+
if (this.onUnmount) {
|
157
|
+
this.onUnmount();
|
158
|
+
}
|
159
|
+
if (this.unsubscribe) {
|
160
|
+
this.unsubscribe();
|
161
|
+
}
|
162
|
+
};
|
163
|
+
|
164
|
+
/**
|
165
|
+
* @deprecated
|
166
|
+
* @see reRender
|
167
|
+
*/
|
168
|
+
triggerRender = () => {
|
169
|
+
this.reRender();
|
170
|
+
};
|
171
|
+
|
172
|
+
/**
|
173
|
+
* ReRenders the component
|
174
|
+
*/
|
175
|
+
reRender = () => {
|
176
|
+
this._renderKey++;
|
177
|
+
render(this.renderTarget, this.render());
|
178
|
+
};
|
179
|
+
}
|
180
|
+
|
181
|
+
return GenericBaseElement;
|
182
|
+
};
|
package/src/common.js
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
function css(strings, ...values) {
|
2
|
+
// Combine the strings and values back into a single string
|
3
|
+
let str = "";
|
4
|
+
strings.forEach((string, i) => {
|
5
|
+
str += string + (values[i] || "");
|
6
|
+
});
|
7
|
+
return str;
|
8
|
+
}
|
9
|
+
|
10
|
+
const breakpoints = ["xs", "sm", "md", "lg", "xl"];
|
11
|
+
|
12
|
+
const styleMap = {
|
13
|
+
mt: "margin-top",
|
14
|
+
mr: "margin-right",
|
15
|
+
mb: "margin-bottom",
|
16
|
+
ml: "margin-left",
|
17
|
+
m: "margin",
|
18
|
+
mh: "margin-left margin-right",
|
19
|
+
mv: "margin-top margin-bottom",
|
20
|
+
pt: "padding-top",
|
21
|
+
pr: "padding-right",
|
22
|
+
pb: "padding-bottom",
|
23
|
+
pl: "padding-left",
|
24
|
+
p: "padding",
|
25
|
+
ph: "padding-left padding-right",
|
26
|
+
pv: "padding-top padding-bottom",
|
27
|
+
g: "gap",
|
28
|
+
gv: "row-gap",
|
29
|
+
gh: "column-gap",
|
30
|
+
bw: "border-width",
|
31
|
+
bwt: "border-top-width",
|
32
|
+
bwr: "border-right-width",
|
33
|
+
bwb: "border-bottom-width",
|
34
|
+
bwl: "border-left-width",
|
35
|
+
bc: "border-color",
|
36
|
+
br: "border-radius",
|
37
|
+
pos: "position",
|
38
|
+
shadow: "box-shadow",
|
39
|
+
ta: "text-align",
|
40
|
+
c: "color",
|
41
|
+
cur: "cursor",
|
42
|
+
};
|
43
|
+
|
44
|
+
export const styleMapKeys = Object.keys(styleMap);
|
45
|
+
|
46
|
+
export const permutateBreakpoints = (keys) => {
|
47
|
+
return keys.concat(
|
48
|
+
breakpoints.flatMap((breakpoint) =>
|
49
|
+
keys.map((key) => `${breakpoint}-${key}`)
|
50
|
+
)
|
51
|
+
);
|
52
|
+
};
|
53
|
+
|
54
|
+
const mediaQueries = {
|
55
|
+
default: undefined,
|
56
|
+
xl: "@media only screen and (max-width: 1280px)",
|
57
|
+
lg: "@media only screen and (max-width: 1024px)",
|
58
|
+
md: "@media only screen and (max-width: 768px)",
|
59
|
+
sm: "@media only screen and (max-width: 640px)",
|
60
|
+
};
|
61
|
+
|
62
|
+
const generateCSS = (styles, descendants = {}) => {
|
63
|
+
let css = "";
|
64
|
+
|
65
|
+
for (const [size, mediaQuery] of Object.entries(mediaQueries)) {
|
66
|
+
if (size !== "default") {
|
67
|
+
css += `${mediaQuery} {`;
|
68
|
+
}
|
69
|
+
for (const [attr, values] of Object.entries(styles)) {
|
70
|
+
const dscendant = descendants[attr] ? ` ${descendants[attr]} ` : " ";
|
71
|
+
for (const [value, rule] of Object.entries(values)) {
|
72
|
+
const cssProperties = styleMap[attr];
|
73
|
+
const cssRule = rule.startsWith("--") ? `var(${rule})` : rule;
|
74
|
+
|
75
|
+
const attributeWithBreakpoint =
|
76
|
+
size === "default" ? attr : `${size}-${attr}`;
|
77
|
+
const hoverAttributeWithBreakpoint =
|
78
|
+
size === "default" ? `h-${attr}` : `${size}-h-${attr}`;
|
79
|
+
|
80
|
+
if (cssProperties) {
|
81
|
+
// Handle multiple properties if mapped in styleMap
|
82
|
+
const properties = cssProperties.split(" ");
|
83
|
+
let propertyRules = properties
|
84
|
+
.map((property) => `${property}: ${cssRule};`)
|
85
|
+
.join(" ");
|
86
|
+
|
87
|
+
css += `
|
88
|
+
:host([${attributeWithBreakpoint}="${value}"])${dscendant}{
|
89
|
+
${propertyRules}
|
90
|
+
}
|
91
|
+
:host([${hoverAttributeWithBreakpoint}="${value}"]:hover)${dscendant}{
|
92
|
+
${propertyRules}
|
93
|
+
}
|
94
|
+
`;
|
95
|
+
} else {
|
96
|
+
// Attribute is not mapped, handle directly
|
97
|
+
css += `
|
98
|
+
:host([${attributeWithBreakpoint}="${value}"])${dscendant}{
|
99
|
+
${rule}
|
100
|
+
}
|
101
|
+
:host([${hoverAttributeWithBreakpoint}="${value}"]:hover)${dscendant}{
|
102
|
+
${rule}
|
103
|
+
}
|
104
|
+
`;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
if (size !== "default") {
|
109
|
+
css += `}`;
|
110
|
+
}
|
111
|
+
}
|
112
|
+
return css;
|
113
|
+
};
|
114
|
+
|
115
|
+
function endsWithDigit(inputValue) {
|
116
|
+
if (inputValue === null) {
|
117
|
+
return false;
|
118
|
+
}
|
119
|
+
if (inputValue.includes("/")) {
|
120
|
+
return false;
|
121
|
+
}
|
122
|
+
// Convert the input value to a string if it's not already one.
|
123
|
+
const inputStr = String(inputValue);
|
124
|
+
// Check if the last character of the string is a digit.
|
125
|
+
return /[0-9]$/.test(inputStr);
|
126
|
+
}
|
127
|
+
|
128
|
+
const endsWithPercentage = (inputStr) => {
|
129
|
+
return /%$/.test(inputStr);
|
130
|
+
};
|
131
|
+
|
132
|
+
const dimensionWithUnit = (dimension) => {
|
133
|
+
if (dimension === undefined) {
|
134
|
+
return;
|
135
|
+
}
|
136
|
+
|
137
|
+
if (endsWithPercentage(dimension)) {
|
138
|
+
return dimension;
|
139
|
+
}
|
140
|
+
|
141
|
+
if (endsWithDigit(dimension)) {
|
142
|
+
return `${dimension}px`;
|
143
|
+
}
|
144
|
+
|
145
|
+
if (Object.keys(spacing).includes(dimension)) {
|
146
|
+
return `var(${spacing[dimension]})`;
|
147
|
+
}
|
148
|
+
|
149
|
+
return dimension;
|
150
|
+
};
|
151
|
+
|
152
|
+
const spacing = {
|
153
|
+
xs: "--spacing-xs",
|
154
|
+
sm: "--spacing-sm",
|
155
|
+
md: "--spacing-md",
|
156
|
+
lg: "--spacing-lg",
|
157
|
+
xl: "--spacing-xl",
|
158
|
+
};
|
159
|
+
|
160
|
+
function convertObjectToCssString(styleObject, selector = ':host') {
|
161
|
+
let result = "";
|
162
|
+
for (const [size, mediaQuery] of Object.entries(mediaQueries)) {
|
163
|
+
if (size !== "default") {
|
164
|
+
result += `${mediaQuery} {\n`;
|
165
|
+
}
|
166
|
+
let cssString = "";
|
167
|
+
for (const [key, value] of Object.entries(styleObject[size])) {
|
168
|
+
if (value !== undefined && value !== null) {
|
169
|
+
cssString += `${key}: ${value};\n`;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
result += `${selector} {
|
173
|
+
${cssString.trim()}
|
174
|
+
}\n`;
|
175
|
+
|
176
|
+
if (size !== "default") {
|
177
|
+
result += `}\n`;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
return result;
|
181
|
+
}
|
182
|
+
|
183
|
+
export {
|
184
|
+
css,
|
185
|
+
generateCSS,
|
186
|
+
dimensionWithUnit,
|
187
|
+
spacing,
|
188
|
+
convertObjectToCssString,
|
189
|
+
mediaQueries,
|
190
|
+
};
|
@@ -0,0 +1,24 @@
|
|
1
|
+
export const INITIAL_STATE = Object.freeze({
|
2
|
+
//
|
3
|
+
});
|
4
|
+
|
5
|
+
export const toViewData = ({ state, props }) => {
|
6
|
+
return {
|
7
|
+
isOpen: props.isOpen,
|
8
|
+
position: {
|
9
|
+
x: 0,
|
10
|
+
y: 0
|
11
|
+
}
|
12
|
+
};
|
13
|
+
}
|
14
|
+
|
15
|
+
export const selectState = ({ state }) => {
|
16
|
+
return state;
|
17
|
+
}
|
18
|
+
|
19
|
+
export const setState = (state) => {
|
20
|
+
// do doSomething
|
21
|
+
}
|
22
|
+
|
23
|
+
|
24
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
elementName: rtgl-dialog
|
2
|
+
|
3
|
+
viewDataSchema:
|
4
|
+
type: object
|
5
|
+
|
6
|
+
propsSchema:
|
7
|
+
type: object
|
8
|
+
properties:
|
9
|
+
isOpen:
|
10
|
+
type: boolean
|
11
|
+
|
12
|
+
refs:
|
13
|
+
dialog-overlay:
|
14
|
+
eventListeners:
|
15
|
+
click:
|
16
|
+
handler: handleClickDialogueOverlay
|
17
|
+
|
18
|
+
events:
|
19
|
+
close-dialogue:
|
20
|
+
type: object
|
21
|
+
properties: {}
|
22
|
+
|
23
|
+
styles:
|
24
|
+
'@keyframes dialog-in':
|
25
|
+
from:
|
26
|
+
opacity: 0
|
27
|
+
transform: scale(0.95)
|
28
|
+
to:
|
29
|
+
opacity: 1
|
30
|
+
transform: scale(1)
|
31
|
+
|
32
|
+
'#dialog-container':
|
33
|
+
animation: dialog-in 150ms cubic-bezier(0.16, 1, 0.3, 1)
|
34
|
+
transform-origin: top
|
35
|
+
|
36
|
+
template:
|
37
|
+
- $if isOpen:
|
38
|
+
- rtgl-view#dialog-overlay pos=fix cor=full ah=c av=c:
|
39
|
+
- rtgl-view#dialog-container pos=fix bw=xs p=lg bgc=bg w=600 br=sm:
|
40
|
+
- slot name=content:
|
@@ -0,0 +1,30 @@
|
|
1
|
+
export const handleOnMount = (deps) => {
|
2
|
+
const { store, props } = deps;
|
3
|
+
store.setDefaultValues(props.defaultValues);
|
4
|
+
};
|
5
|
+
|
6
|
+
export const handleActionClick = (e, deps) => {
|
7
|
+
const { store, dispatchEvent } = deps;
|
8
|
+
const id = e.currentTarget.id.replace("action-", "");
|
9
|
+
dispatchEvent(
|
10
|
+
new CustomEvent("action-click", {
|
11
|
+
detail: {
|
12
|
+
actionId: id,
|
13
|
+
formValues: store.selectFormValues(),
|
14
|
+
},
|
15
|
+
}),
|
16
|
+
);
|
17
|
+
};
|
18
|
+
|
19
|
+
export const handleInputChange = (e, deps) => {
|
20
|
+
const { store } = deps;
|
21
|
+
const id = e.currentTarget.id.replace("input-", "");
|
22
|
+
// TODO fix double event
|
23
|
+
if (id && e.detail.value !== undefined) {
|
24
|
+
store.setFormFieldValue({
|
25
|
+
// TODO user field name instead of id
|
26
|
+
fieldName: id,
|
27
|
+
value: e.detail.value,
|
28
|
+
});
|
29
|
+
}
|
30
|
+
};
|
@@ -0,0 +1,45 @@
|
|
1
|
+
export const INITIAL_STATE = Object.freeze({
|
2
|
+
formValues: {}
|
3
|
+
});
|
4
|
+
|
5
|
+
const blacklistedAttrs = ['id', 'class', 'style', 'slot'];
|
6
|
+
|
7
|
+
const stringifyAttrs = (attrs) => {
|
8
|
+
return Object.entries(attrs).filter(([key]) => !blacklistedAttrs.includes(key)).map(([key, value]) => `${key}=${value}`).join(' ');
|
9
|
+
}
|
10
|
+
|
11
|
+
export const toViewData = ({ state, props, attrs }) => {
|
12
|
+
const containerAttrString = stringifyAttrs(attrs);
|
13
|
+
return {
|
14
|
+
containerAttrString,
|
15
|
+
title: props.form?.title || '',
|
16
|
+
description: props?.form?.description || '',
|
17
|
+
fields: props?.form?.fields || [],
|
18
|
+
actions: props?.form?.actions || {
|
19
|
+
buttons: []
|
20
|
+
},
|
21
|
+
// TODO fix default values
|
22
|
+
formValues: state.formValues,
|
23
|
+
};
|
24
|
+
}
|
25
|
+
|
26
|
+
export const selectState = ({ state }) => {
|
27
|
+
return state;
|
28
|
+
}
|
29
|
+
|
30
|
+
export const selectFormValues = ({ state }) => {
|
31
|
+
return state.formValues;
|
32
|
+
}
|
33
|
+
|
34
|
+
export const setState = (state) => {
|
35
|
+
// do doSomething
|
36
|
+
}
|
37
|
+
|
38
|
+
export const setDefaultValues = (state, defaultValues) => {
|
39
|
+
state.formValues = defaultValues || {};
|
40
|
+
}
|
41
|
+
|
42
|
+
export const setFormFieldValue = (state, { fieldName, value }) => {
|
43
|
+
state.formValues[fieldName] = value;
|
44
|
+
}
|
45
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
elementName: rtgl-form
|
2
|
+
|
3
|
+
viewDataSchema:
|
4
|
+
type: object
|
5
|
+
|
6
|
+
propsSchema:
|
7
|
+
type: object
|
8
|
+
properties:
|
9
|
+
defaultValues:
|
10
|
+
type: object
|
11
|
+
properties: {}
|
12
|
+
form:
|
13
|
+
type: object
|
14
|
+
properties:
|
15
|
+
title:
|
16
|
+
type: string
|
17
|
+
description:
|
18
|
+
type: string
|
19
|
+
|
20
|
+
refs:
|
21
|
+
action-*:
|
22
|
+
eventListeners:
|
23
|
+
click:
|
24
|
+
handler: handleActionClick
|
25
|
+
input-*:
|
26
|
+
eventListeners:
|
27
|
+
input-keydown:
|
28
|
+
handler: handleInputChange
|
29
|
+
|
30
|
+
events: {}
|
31
|
+
|
32
|
+
template:
|
33
|
+
- rtgl-view w=f h=f p=md g=lg ${containerAttrString}:
|
34
|
+
- rtgl-view g=sm w=f:
|
35
|
+
- rtgl-text s=lg: ${title}
|
36
|
+
- rtgl-text c=mu-fg: ${description}
|
37
|
+
- rtgl-view g=sm w=f:
|
38
|
+
- $for field, i in fields:
|
39
|
+
- rtgl-view g=sm w=f:
|
40
|
+
- rtgl-text: ${field.label}
|
41
|
+
- rtgl-text s=sm c=mu-fg: ${field.description}
|
42
|
+
- $if field.inputType == "inputText":
|
43
|
+
- rtgl-input#input-${field.id} w=f:
|
44
|
+
- rtgl-view g=sm w=f:
|
45
|
+
- $for button, i in actions.buttons:
|
46
|
+
- rtgl-view d=h ah=e g=sm w=f:
|
47
|
+
- rtgl-button#action-${button.id}: ${button.content}
|
@@ -0,0 +1,86 @@
|
|
1
|
+
config: {}
|
2
|
+
|
3
|
+
---
|
4
|
+
name: 'basic_with_href'
|
5
|
+
viewData:
|
6
|
+
start:
|
7
|
+
label: "My App"
|
8
|
+
href: "/"
|
9
|
+
|
10
|
+
---
|
11
|
+
name: 'with_path_navigation'
|
12
|
+
viewData:
|
13
|
+
start:
|
14
|
+
label: "Dashboard"
|
15
|
+
path: "/dashboard"
|
16
|
+
|
17
|
+
---
|
18
|
+
name: 'with_logo_image'
|
19
|
+
viewData:
|
20
|
+
start:
|
21
|
+
label: "Company Name"
|
22
|
+
image:
|
23
|
+
src: "/logo.png"
|
24
|
+
width: 32
|
25
|
+
height: 32
|
26
|
+
alt: "Company Logo"
|
27
|
+
|
28
|
+
---
|
29
|
+
name: 'with_logo_and_href'
|
30
|
+
viewData:
|
31
|
+
start:
|
32
|
+
label: "Rettangoli"
|
33
|
+
href: "/"
|
34
|
+
image:
|
35
|
+
src: "/assets/logo.svg"
|
36
|
+
width: 40
|
37
|
+
height: 40
|
38
|
+
alt: "Rettangoli Logo"
|
39
|
+
|
40
|
+
---
|
41
|
+
name: 'with_logo_and_path'
|
42
|
+
viewData:
|
43
|
+
start:
|
44
|
+
label: "Project Hub"
|
45
|
+
path: "/projects"
|
46
|
+
image:
|
47
|
+
src: "/icons/hub.png"
|
48
|
+
width: 28
|
49
|
+
height: 28
|
50
|
+
alt: "Hub Icon"
|
51
|
+
|
52
|
+
---
|
53
|
+
name: 'text_only_no_navigation'
|
54
|
+
viewData:
|
55
|
+
start:
|
56
|
+
label: "Static Header"
|
57
|
+
|
58
|
+
---
|
59
|
+
name: 'long_label_with_image'
|
60
|
+
viewData:
|
61
|
+
start:
|
62
|
+
label: "Enterprise Resource Planning System"
|
63
|
+
image:
|
64
|
+
src: "/brand/enterprise-logo.jpg"
|
65
|
+
width: 36
|
66
|
+
height: 36
|
67
|
+
alt: "Enterprise Logo"
|
68
|
+
|
69
|
+
---
|
70
|
+
name: 'minimal_brand'
|
71
|
+
viewData:
|
72
|
+
start:
|
73
|
+
label: "Brand"
|
74
|
+
href: "/home"
|
75
|
+
|
76
|
+
---
|
77
|
+
name: 'app_with_square_logo'
|
78
|
+
viewData:
|
79
|
+
start:
|
80
|
+
label: "TaskFlow"
|
81
|
+
path: "/tasks"
|
82
|
+
image:
|
83
|
+
src: "/logos/taskflow-square.png"
|
84
|
+
width: 30
|
85
|
+
height: 30
|
86
|
+
alt: "TaskFlow Square Logo"
|