diff --git a/kastro/README.md b/kastro/README.md index 68e35df..a5e9621 100644 --- a/kastro/README.md +++ b/kastro/README.md @@ -27,20 +27,20 @@ compared to dynamically creating elements one by one via JavaScript like many other frameworks do. ## Example -```jsx filename="LandingPage.jsx" +```jsx:LandingPage.jsx import dom from "@kimlikdao/util/dom"; import { LangCode } from "@kimlikdao/util/i18n"; import ArrowSvg from "./arrow.svg"; import Css from "./LandingPage.css"; /** - * @param {{ Lang: LangCode }} Lang + * @param {{ Lang: LangCode }} props */ const LandingPage = ({ Lang }) => { /** @const {!HTMLButtonElement} */ - const Button = dom.button(Css.ButtonId); + const Button = dom.button(Css.Button); /** @const {!HTMLSpanElement} */ - const Text = dom.span(Css.TextId); + const Text = dom.span(Css.Text); return ( @@ -74,13 +74,13 @@ would resolve to something like ` c.innerText = "Clicked!"; ``` and the following html will be generated (after de-minification): -```html +```html:build/LandingPage-en.html @@ -113,7 +113,7 @@ or create their own for the particular DOM interaction patterns they need. A typical Kastro component code will be declarative, intuitive and boilerplate free. -## Components +## Intro to Kastro components In Kastro, components are function objects: the function part is used to render the component html and setup the DOM bindings while the object part is used to @@ -137,7 +137,8 @@ These components can keep an internal state, however if there are multiple copies of the component in a page, they will share the same state. This means that for singleton components, we can freely keep internal state, however for reusable components, either the entire state must be kept in the DOM -or passed into the methods of the component by the caller. +or passed into the methods of the component by the caller. At this point, +one can consider using a stateful component instead. Kastro compiler will generate the `Component({ id: "idAssignedByParent" })` invocations from the initialization code of the parent component. @@ -177,7 +178,8 @@ Page(); // The root component is auto initialized by Kastro transpiler ### 2. Stateful A component which takes an `instance` property is deemed stateful. These components can keep an internal state and for each copy of the component, a -class instance is created. +class instance is created and bound to the variable passed as `instance` +property. Note the `instance` property is used by the client jsx transpiler and never passed to the component itself. @@ -272,6 +274,11 @@ const Page = () => ( Currently we do not allow attaching event handlers to grand children of pseudo components to encourage more maintainable code. +## Components + + + + ## Kastro type system Kastro leverages the type system of `kdjs` to provide both type safety and diff --git a/kastro/examples/LandingPage.css b/kastro/examples/LandingPage.css new file mode 100644 index 0000000..8e1c09b --- /dev/null +++ b/kastro/examples/LandingPage.css @@ -0,0 +1,5 @@ +/** @export */ +#Button {} + +/** @export */ +#Text {} \ No newline at end of file diff --git a/kastro/examples/LandingPage.jsx b/kastro/examples/LandingPage.jsx new file mode 100644 index 0000000..9470e47 --- /dev/null +++ b/kastro/examples/LandingPage.jsx @@ -0,0 +1,26 @@ +import dom from "@kimlikdao/util/dom"; +import { LangCode } from "@kimlikdao/util/i18n"; +import ArrowSvg from "./arrow.svg"; +import Css from "./LandingPage.css"; + +/** + * @param {{ Lang: LangCode }} props + */ +const LandingPage = ({ Lang }) => { + /** @const {!HTMLButtonElement} */ + const Button = dom.button(Css.ButtonId); + /** @const {!HTMLSpanElement} */ + const Text = dom.span(Css.TextId); + + return ( + + + + Hello World! + + ); +}; + +export default LandingPage; diff --git a/kastro/transpiler/jsx.js b/kastro/transpiler/jsx.js index 66f174a..0b558ea 100644 --- a/kastro/transpiler/jsx.js +++ b/kastro/transpiler/jsx.js @@ -209,6 +209,7 @@ const transpile = (isEntry, file, content, domIdMapper, globals) => { put: css.getEnum(file, strippedCss, domIdMapper) }); } + /** * @param {!acorn.Node} node * @param {!acorn.Node} parent @@ -226,6 +227,21 @@ const transpile = (isEntry, file, content, domIdMapper, globals) => { processInlineCss(node, parent); return; } + } else if (node.type == "ObjectExpression") { + // Remove name$ properties from all object expressions. + for (let i = 0; i < node.properties.length; ++i) { + const prop = node.properties[i]; + if (prop.key.type === "Identifier" && prop.key.name.endsWith("$")) { + const end = i < node.properties.length - 1 + ? node.properties[i + 1].start // Remove up to next property + : prop.end + 1; // Last property + updates.push({ + beg: prop.start, + end: end, + put: "" + }); + } + } } else if (node.type === "JSXElement" || node.type === "JSXFragment") { const statements = []; processJsxElement(node, parent, 0, statements);