{"id":21854735,"url":"https://github.com/logantann/macon","last_synced_at":"2026-03-03T01:37:21.696Z","repository":{"id":264967000,"uuid":"858806149","full_name":"LoganTann/macon","owner":"LoganTann","description":"Tiny utility (430B) to create reactive jquery components using a JSX-like syntax","archived":false,"fork":false,"pushed_at":"2025-01-22T13:45:08.000Z","size":39,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-05T19:47:21.838Z","etag":null,"topics":["dom","html","jquery","jsx","template-literals","utility","view","web-components"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LoganTann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-17T15:08:12.000Z","updated_at":"2025-01-22T13:45:12.000Z","dependencies_parsed_at":"2024-11-27T01:38:52.953Z","dependency_job_id":"1156084a-e2b7-449c-a359-374f842a5dd4","html_url":"https://github.com/LoganTann/macon","commit_stats":null,"previous_names":["logantann/macon"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LoganTann%2Fmacon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LoganTann%2Fmacon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LoganTann%2Fmacon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LoganTann%2Fmacon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LoganTann","download_url":"https://codeload.github.com/LoganTann/macon/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248931877,"owners_count":21185260,"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":["dom","html","jquery","jsx","template-literals","utility","view","web-components"],"created_at":"2024-11-28T02:10:44.342Z","updated_at":"2026-03-03T01:37:21.649Z","avatar_url":"https://github.com/LoganTann.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eMaçon\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\u003cb\u003eTiny utility to create reactive jQuery components using a JSX-like syntax\u003c/b\u003e\u003c/p\u003e\n\n\u003cspan\u003e\u0026nbsp;\u003c/span\u003e\n\n\u003e Yet another javascript framework ?\n\nPeople hate working with large jQuery codebase, because it scales very badly. Using a component-based approach is the solution.\n\nBut integrating declarative MVVM frameworks like vue.js, preact or LWC may be impossible for your project, since it will require you to nuke your codebase and rebuild everything using that framework.\n\nMaçon aims to **incrementally refactor your website into reactive components**, while staying integrated with your jQuery codebase.\n\n## Install\n\nCopy-paste the `template` function located in [main.js](./main.js).\n\nThe framwework stands in a single function, as it is an ES6 tagged template processor.\n\nMinified and gzipped size is 430B. Although I suggest copy pasting the function with JSDoc comments so you can benefit from proper editor integration.\n\n## Usage\n\n### Just add `template` before your string, and the magic happens\n\nCreate nested DOM element, enhanced with jquery. Append other elements by interpolating them, just like JSX. Call jquery methods like you're used to.\n\n```javascript\nimport { template } from \"./macon.js\";\n\nconst component = template`\u003cbutton id=\"nextBtn\" type=\"button\"\u003eNext\u003c/button\u003e`;\ncomponent.on(\"click\", function(event) {\n    event.preventDefault();\n    console.log(\"Button clicked !\");\n});\n\n$(\"#anywhere\").append(template`\n    \u003cmain\u003e\n        \u003cdiv class=\"button-container\"\u003e\n            ${component}\n        \u003c/div\u003e\n    \u003c/main\u003e\n`);\n```\n\n### Loops and conditions\n\nWe've seen that the template utility supports strings and jquery objects as interpolated values. It can also accept arrays.\n\nTherefore, conditionnals and loops can be created like how we do in React, using ternaries and the `Array.map` method :\n\n```js\nimport { template } from \"./macon.js\";\n\n$(\"#anywhere\").append(ShoppingListComponent());\n\n\n// is that event jquery ??\nfunction ShoppingListComponent() {\n    const items = [[\"Wheat\", true], [\"Eggs\", false], [\"Milk\", false]];\n    return template`\n        \u003cdiv\u003e\n            ${items.map(([name, checked], index) =\u003e listItem({name, checked, index}))}\n        \u003c/div\u003e\n    `;\n}\n\nfunction listItem({name, checked, index}) {\n    return template`\n        \u003cdiv class=\"custom-checkbox\"\u003e\n            \u003cinput type=\"checkbox\" id=\"checkbox-${index}\" ${checked ? \"checked\" : \"\"}/\u003e\n            \u003clabel for=\"checkbox-${index}\"\u003e${name}\u003c/label\u003e\n        \u003c/div\u003e\n    `\n}\n```\n\nWhen a callback is passed as interpolated value, the template utility will execute it. \n\n```js\n/**\n * Shows a recipe\n * @param {object} props\n * @param {boolean} props.isLoaded Set to false if the recipe is still loading.\n * @param {string|JqueryComponent} props.value Recipe html content\n * @return {JqueryComponent}\n */\nfunction RecipeLoaderComponent({isLoaded, value}) {\n    const component = template`\n        \u003cdiv\u003e\n            ${() =\u003e {\n                if (isLoaded) {\n                    return `\u003cdiv\u003e${value}\u003c/div\u003e`;\n                }\n                return `Loading...`;\n            }}\n        \u003c/div\u003e\n    `;\n    return component;\n}\n```\n\n### Adding reactive state\n\nWhile it's possible to edit the component's content using regular jquery methods, and event edit other components state, you should write them in a declarative manner. Like Vue.js 2, it's advised to keep the component's own state inside the state variable, or into a model that is passed as parameter. \n\nThe template utility does not have a built-in variable watcher, so to update it when a variable changes, you need to manually call the `component.refresh()` function. This will re-execute all callbacks passed in interpolated values. Therefore, to make a variable reactive, you should add it to a lambda function.\n```js\nfunction chronoComponent() {\n    const state = {\n        mutableValue: 0,\n        get computedValue() {\n            return new Date().toLocaleTimeString();\n        }\n    };\n    const component = template`\n        \u003cdiv\u003e\n            \u003cdiv\u003eCreated time (won't be refreshed, this is not a callback !!): ${state.computedValue}\u003c/div\u003e\n            \u003cdiv\u003eCurrent time (reactive value since it's inside a callback) : ${() =\u003e state.computedValue}\u003c/div\u003e\n            \u003cdiv\u003eElapsed seconds since created : ${() =\u003e state.mutableValue}\u003c/div\u003e\n        \u003c/div\u003e\n    `;\n    setInterval(() =\u003e {\n        state.mutableValue += 1;\n        component.refresh();\n    }, 1000);\n    return component;\n}\n```\n\n\u003e ⚠️ Note: Although you can include multiple root elements, the HTML code for the component should contain only one root element to be properly reactive.\n\n### Event and reactive state management\n\nFor child to parent communication, pass callback as parameters like in React.\n\nYou can use jquery's custom event system for global or parent-to-child communication. \n\nSince the template function returns a jquery node, you can use the `component.find(\"selector\").on( )` method to add an event to a nested item in the component's template.\nHowever, please keep in mind that calling refresh() will delete the component's children, and so the targeted element. This is much better to create a separate component that will send an event to the parent.\n\nAt work, I created a component called `SubscribeEvent(name, handler)` and a function `sendEvent(name)` to abstract this job and detect changes.\n\nYou can also use javascript proxies to detect mutations and call refresh() at the correct time.\n\n## Contributing \n\nThe name \"Maçon\" (which means \"mason\" or \"builder\" in French) is derived from the [façon library](https://github.com/terkelg/facon), which served as a source of inspiration for this project.\n\nThis is an utility I created for my internship to solve a specific issue (convert an existing JQuery codebase MVVM architecture). It has been successfully used to develop a feature-rich MOOC platform.\n\nGiven the professional context, I got the authorization to make it open-source, but I don't plan use my free time to add new features.\n\nStill, contributions are very welcome and I'll do my best to answer pull requests, especially if it's about adding CJS+ESM support, unit tests, CI and npm release.\n\n## API\n\n### `JqueryComponent` type definition\n\nReturn value of the template() function. Aliases to `JQuery\u003cHTMLElement\u003e \u0026 {refresh: () =\u003e void}`;\n\n### `template(...string)`\n\nReturns: `JqueryComponent`\n\nConstruct and returns a Jquery DOM element.\n\nThe returned element has a special refresh method that is used to process again interpolated values. \n\nStrings, Arrays, DOM Elements, jQuery elements and callback can be composed together/appended like this :\n\n```js\n// Concatenated\nlet htmlString = `\u003cp\u003eSome HTML\u003c/p\u003e;\nlet node = template`\u003cdiv\u003e${htmlString}\u003c/div\u003e`;\n\n// Appended\nlet myNode = document.createElement('div');\nlet node = template`\u003cdiv\u003e${myNode}\u003c/div\u003e`;\n```\n\n### `JqueryComponent.refresh()`\n\nMethod to refresh a component's state. \n\nBe aware that there is no Virtual DOM nor smart updates when calling `refresh()`.\n\nThe utility will naively delete the node's content, re-build the HTML and insert it. Therefore, using reactivity to use CSS transitions is not possible.\n\n\n## License\n\nMIT © [ShinProg](https://github.com/LoganTann)\n\nInspired from the [terkelg/facon](https://github.com/terkelg/facon/) project, © [Terkel Gjervig](https://terkel.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogantann%2Fmacon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flogantann%2Fmacon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogantann%2Fmacon/lists"}