{"id":22332821,"url":"https://github.com/craftkit/craft-uikit","last_synced_at":"2025-07-13T15:33:43.537Z","repository":{"id":56652381,"uuid":"175121525","full_name":"craftkit/craft-uikit","owner":"craftkit","description":"Clear Web OOP for Generation WebComponents","archived":false,"fork":false,"pushed_at":"2020-10-27T05:25:52.000Z","size":480,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-02T16:18:18.999Z","etag":null,"topics":["encapsulated-styling","frontend-webdevelopment","javascript-framework","javascript-library","micro-frontend","progressive-web-app","shadow-dom","single-page-applications","ui-components","webcomponents"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/craftkit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-12T02:41:28.000Z","updated_at":"2022-05-12T05:52:48.000Z","dependencies_parsed_at":"2022-08-15T22:40:26.442Z","dependency_job_id":null,"html_url":"https://github.com/craftkit/craft-uikit","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/craftkit/craft-uikit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftkit%2Fcraft-uikit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftkit%2Fcraft-uikit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftkit%2Fcraft-uikit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftkit%2Fcraft-uikit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/craftkit","download_url":"https://codeload.github.com/craftkit/craft-uikit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftkit%2Fcraft-uikit/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265163074,"owners_count":23720895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["encapsulated-styling","frontend-webdevelopment","javascript-framework","javascript-library","micro-frontend","progressive-web-app","shadow-dom","single-page-applications","ui-components","webcomponents"],"created_at":"2024-12-04T04:19:56.887Z","updated_at":"2025-07-13T15:33:43.491Z","avatar_url":"https://github.com/craftkit.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Craft-UIKit\n\nCraft-UIKit is a JavaScript UI library for Clear Web OOP.\n\nTry online tutorial:  \n[https://github.com/craftkit/craftkit-playground](https://github.com/craftkit/craftkit-playground)\n\n## Component\n\n![component](assets/component.png)\n\nThe most core element of Craft-UIKit is representedy by the base class `Craft.Core.Component`.\nThis wraps shadow host, shadow root, tree under the shadow, styles for them and its actions.\n\nInstance of Component is identified by `componentId`. \nBy default, this is automatically generated by its `packagename` defined as its class variable, with appending sequencial number to be able to identify in the global scope. \nIf it is not defined, just used its class name for it.\n\nThe component instance is a JavaScript object. \nSo, you can access to the instance via its `componentId` from anywhere you want.\n\n### example\n\nFor example, your class has packagename `MyApp.Hello`.  \nYour **componentId** will be something like `MyApp_Hello_0`.\n\n``` \nconsole:\n\n\u003e var hello_world = new HelloWorld();\n\u003e hello_world.loadView();\n\u003e document.body.appendChild(hello_world.view);\n\n\u003e MyApp_HelloWorld_0\nHelloWorld {packagename: \"MyApp.HelloWorld\", componentId: \"MyApp_HelloWorld_0\", view: div#MyApp_HelloWorld_0, css: Array(1), …}\n\n\u003e MyApp_HelloWorld_0.view\n\u003cdiv id=\"MyApp_HelloWorld_0\"\u003e...\u003c/div\u003e\n\n\u003e MyApp_HelloWorld_0.shadow\n#shadow-root (open)\n\n\u003e MyApp_HelloWorld_0.root\n\u003cdiv class=\"root\"\u003e...\u003c/div\u003e\n\n\u003e MyApp_HelloWorld_0.css\n[style#MyApp_HelloWorld_0_1]\n\n\u003e MyApp_HelloWorld_0.say()\n(say() is invoked -\u003e show \"Hello World!\" in the page)\n``` \n\n※ Dot(`.`) is converted to underscore(`_`).  \n※ componentId has suffix of auto generated serial number.\n\n\n## View and ViewController\n\n![component](assets/tree.png)\n\nThe Component is concretized by View(`Craft.UI.View`) and ViewController(`Craft.UI.DefaultViewController`). \nBoth are sub-class of `Craft.Core.Component`.\nView is an element packing its template, style and action. \nViewController is also a kind of View having some View elements in it, and manages them.\n\nYour application may also have special ViewController called RootViewController(`Craft.UI.DefaultRootViewController`). \nThis is a root element of your application, \nand should be set by `Craft.Core.Context.setRootViewController` at the start of your application. \nIn Craft-UIKit application, the RootViewController should also manage popstate event and history by implementing appropriate interface. \n\nLet's see a Hello World example:\n\n```javascript \nclass HelloWorld extends Craft.UI.DefaultViewController {\n    constructor(){\n        super();\n        this.packagename = 'MyApp.HelloWorld';\n    }\n    viewDidLoad(callback){\n        this.hello = new Hello();\n        this.world = new World();\n        if(callback){ callback(); }\n    }\n    say(){\n        this.appendView(this.hello);\n        this.appendView(this.world);\n    }\n    style(componentId){\n        return `\n            :host { width: 100%; }\n            .root { display flex; flex-direction: row; }\n        `;\n    }\n    template(componentId){\n        return `\n            \u003cdiv class='root'\u003e\u003c/div\u003e\n        `;\n    }\n}\n\nclass Msg extends Craft.UI.InlineBlockView {\n    style(componentId){\n        return `\n            .root { margin: 10px; }\n            .msg { color: blue; }\n        `;\n    }\n    template(componentId){\n        return `\n            \u003cdiv class='root'\u003e\n                \u003cspan class='msg'\u003e${this.msg}\u003c\\span\u003e\n            \u003c/div\u003e\n        `;\n    }\n}\n\nclass Hello extends Msg {\n    constructor(){\n        super();\n        this.packagename = 'MyApp.Hello';\n        this.msg = 'Hello';\n    }\n}\n\nclass World extends Msg {\n    constructor(){\n        super();\n        this.packagename = 'MyApp.World';\n        this.msg = 'World!';\n    }\n    style(componentId){\n        return super.style(componentId) + `\n            .msg { color: red; }\n        `;\n    }\n}\n``` \n\n## Template and Style\n\nComponent has a DOM tree in `this.root`. This is based on `template()`.\n\n`template()` must return a HTML expression that starting with a single element. \nThe returning HTML is evalutated as HTML template, \nand converted to a DOM fragment by Craft.Core.Component.renderView. \nIts first element is used for **this.root**. \n\nWhen you define `id` and `class` for the root element, you have to name it as 'root'. \nThis will simplify to cascade style-sheet of super class.\n\n```javascript \ntemplate(componentId){\n    return `\n        \u003cdiv id='root' class='root'\u003e\n            ...\n        \u003c/div\u003e\n    `;\n}\n``` \n\n```html\nGOOD:\n    \u003cdiv\u003e\n        \u003cspan\u003eHello world!\u003c/span\u003e\n    \u003c/div\u003e\n    \nBAD:\n    \u003cdiv\u003e\n        \u003cspan\u003eHello\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv\u003e\n        \u003cspan\u003eworld!\u003c/span\u003e\n    \u003c/div\u003e\n    \nAlso BAD: \n    \u003c!-- hello message: this is also a DOM --\u003e\n    \u003cdiv\u003e\n        \u003cspan\u003eHello world!\u003c/span\u003e\n    \u003c/div\u003e\n\n```\n\nComponent also has shadowed style tags in **this.css**.\nThis is defined by `style()` method and it should return usual style sheet expression.\nYou can access host element **this.view** by `:host` pseudo class name. \n\nTo cascade super class style, just append yours on it.\n\n```javascript \nstyle(){\n    return super.style() + `\n        .msg { color: red; }\n    `;\n}\n``` \n\nFirst argument for `template()` and `style()` is its componentId (same as this.componentId).\nIn `style` method, you may not use it, but sometimes it may be required to cascade styles from your super class. \nIn `template` method, it is used for accessing its method.\n\n\n## Component Method\n\nTo call instance method from its template, all you have to do is just call it via `${componentId}`.\n\n```javascript \nclass Wow extends Craft.UI.View {\n    say(msg){\n        alert(`oh ${msg}`);\n    }\n    template(componentId){\n        return `\n            \u003cdiv onclick='${componentId}.say(\"wow\")'\u003eSay wow\u003c/div\u003e\n        `;\n    }\n}\n``` \n\nTraditional JavaScript programmer may like `self` instead of `componentId`.\n\n```javascript \nclass Wow extends Craft.UI.View {\n    say(msg){\n        alert(`oh ${msg}`);\n    }\n    template(self){\n        return `\n            \u003cdiv onclick='${self}.say(\"wow\")'\u003eSay wow\u003c/div\u003e\n        `;\n    }\n}\n``` \n\nIndeed, this is not default behaviour. \nAs can be seen above, `window[componentId]` holds its instacne. \nThis is enabled by setting `Craft.Core.Defaults.ALLOW_COMPONENT_SHORTCUT` to `true`. \nYou may set this at the begenning of you app. \nYou can select this behavior, but you may love to use this shortcut.\n\nWithout this shourcut, you can call instance method like following:\n\n```javascript \nonclick=\"window.Craft.Core.ComponentStack.get('${componentId}').say('wow')\"\n``` \n\nAdditionaly, if you know componentId for another instance, you can call any method via it, like as global shared function.\n\nPublic library developer shoud write its template by verbose mode using fully quolifiied component access, to be able to run without shourcut. \n\n\n## Component Lifecycle\n\nComponent lifecycle is just a contract with you. \nIf you write your own ViewController, you have to honor those definition to keep your life safe, \nlike as Craft.Core.Component, Craft.UI.View and Craft.UI.DefaultViewController are doing so. \nIn other words, while you extends those classes and keep this way, lifecycle is guaranteed.\n\n![lifecycle](assets/lifecycle.png)\n\n| lifecycle method  | what is  |\n|:------------------|:---------|\n| loadView          | make this.view and this.css |\n| viewDidLoad       | Called at the end of loadView |\n| viewWillAppear    | Called just before appending this.view to the parent |\n| viewDidAppear     | Called just after this.view appended to the parent |\n| viewWillDisappear | Called just before removing this.view from its parent |\n| viewDidDisappear  | Called just after this.view removed from its parent |\n| unloadView        | remove view and css |\n\nRelated method:\n\n| method        | what is    |\n|:--------------|:-----------|\n| appendSubView | append sub-component view | \n| removeSubView | remove sub-component view | \n| removeFromParent | remove component view from parent |\n\n\n## Routing and RootViewController\n\n![routing](assets/routing.png)\n\nIn Craft-UIKit application, RootViewController has responsibility for routing. \nAt the time booting application via `Craft.Core.Bootstrap`, \nlistener for `popstate` is registered against `RootViewController.didReceivePopstate`. \n\nThe recieved PopState event is passed for Router.\nDefault router is `Craft.Core.HashRouter`, using '#' for routing component.\n\nYou can define your original router in your application config object like described in the next section. \nCraft-UIKit provides both HashRouter for '#' and PathRouter for '/'. The former is default.\n\nRouter parses the PopState event and window.location, then pass it to `resolveRoutingRequest`. \nYou have to implement your own routing logic in it. Or you have to override `DefaultRootViewController.didReceivePopstate` as you like.\n\n```javascript \nclass PageController extends Craft.UI.DefaultRootViewController {\n    :\n    resolveRoutingRequest(route){\n        switch(route.path){\n            case 'page1':\n                this.open({ page:new Page1() });\n                break;\n            case 'page2':\n                this.open({ page:new Page2() });\n                break;\n            default:\n                this.open({ page:new NotFound() });\n                break;\n\t\t}\n    }\n    :\n}\n``` \n\n## Booting application\n\nApplication entry point is `Craft.Core.Bootstrap.boot`. \nYou must kick this function when `window.onload` occured. \n\n`boot` function requires an object containing a function named as `didBootApplication`. \nThis is your application entry point. \n\nTo start routing at booting time, you have to call `didReceivePopstate` of your RootViewController. \nFor convenience, this is implemented as `DefaultRootViewController.bringup`.\n\n```javascript \nwindow.onload = function(){\n    Craft.Core.Defaults.ALLOW_COMPONENT_SHORTCUT = true;\n    Craft.Core.Bootstrap.boot({\n        router : Craft.Core.PathRouter,\n        didBootApplication : function(){\n            const rootViewController = new PageController();\n            Craft.Core.Context.setRootViewController(rootViewController);\n            rootViewController.bringup();\n        }\n    });\n};\n``` \n\n## License\n\nMIT\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcraftkit%2Fcraft-uikit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcraftkit%2Fcraft-uikit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcraftkit%2Fcraft-uikit/lists"}