{"id":18975505,"url":"https://github.com/gluecodes/gluecodes-glue-dom","last_synced_at":"2025-04-19T16:52:38.261Z","repository":{"id":42659338,"uuid":"128650899","full_name":"gluecodes/gluecodes-glue-dom","owner":"gluecodes","description":"Makes non-trivial UI components easy to read and maintain.","archived":false,"fork":false,"pushed_at":"2023-01-07T06:26:48.000Z","size":1272,"stargazers_count":7,"open_issues_count":7,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T10:33:28.211Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.glue.codes","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/gluecodes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-08T14:42:50.000Z","updated_at":"2022-05-20T10:05:34.000Z","dependencies_parsed_at":"2023-02-06T14:00:47.827Z","dependency_job_id":null,"html_url":"https://github.com/gluecodes/gluecodes-glue-dom","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gluecodes%2Fgluecodes-glue-dom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gluecodes%2Fgluecodes-glue-dom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gluecodes%2Fgluecodes-glue-dom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gluecodes%2Fgluecodes-glue-dom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gluecodes","download_url":"https://codeload.github.com/gluecodes/gluecodes-glue-dom/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249224003,"owners_count":21232850,"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":[],"created_at":"2024-11-08T15:19:20.678Z","updated_at":"2025-04-16T09:34:48.253Z","avatar_url":"https://github.com/gluecodes.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @gluecodes/glueDom\n\nMakes non-trivial UI components easy to read and maintain.\n\nIt has been made to abstract-away [SolidJS'](http://solidjs.com/) HyperScript to generate reactive code in [GlueCodes Studio](https://ide.glue.codes) on the fly.\n\n\u003cp align=\"center\"\u003e\u003cimg width=\"100%\" src=\"https://github.com/gluecodes/gluecodes-glue-dom/blob/master/glue-dom.png\" alt=\"GlueDOM\"\u003e\u003c/p\u003e\n\n- It can be transformed in maintainable and scalable manner.\n- Gradual learning curve, no need to learn another templating syntax (directives etc.).\n- Reads sequentially as HTML while remaining readable and maintainable.\n- Isn't a mix of HTML and JavaScript drawing a clear border between view and logic.\n- Allows formatting texts without writing nested inline tags.\n- Makes writing dynamic texts easier with no need for checking whether variables are non-empty.\n\n## Table of Contents\n\n- [Problem](#problem)\n- [Syntax Comparison](#syntax-comparison)\n  - [JSX](#jsx)\n  - [HyperScript](#hyperscript)\n  - [GlueDOM](#gluedom)\n- [Installation](#installation)\n- [Basic Usage](#basic-usage)\n- [Advanced Usage](#advanced-usage)\n- [GlueDOM Syntax](#gluedom-syntax)\n  - [Rendering a Tag](#rendering-a-tag)\n  - [Inside Tag Callback](#inside-tag-callback)\n- [Api](#api)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Problem\n\nThe ideal syntax for rendering DOM should mimic HTML in a way it reads sequentially from top to bottom. \nFor single logical UI unit, there shouldn't be a need for local variable declarations or using partial functions. \n\nConsider the two most common ways of rendering DOM; JSX and HyperScript. They work well in simple demo scenarios with no nested conditional logic. \nWhen the nested conditionals are required, you end up using a mix of logical expressions, ternary and spread operators.\n\n## Syntax Comparison\n\nConsider the following example; Write a function which renders condition-based HTML. \nThere is `someCondition` prop which needs to be truthy to display a section which contains other nested conditionals. \n`firstProgrammer` and `secondProgrammer` are both optional.\n\n### JSX\n\nSince you can't use block statements, you're forced to use unreadable mix of logical and ternary operators.\n\n```jsx\n({ \n  firstProgrammer,\n  secondProgrammer,\n  someCondition\n}) =\u003e (\n  \u003cdiv\u003e\n    {someCondition\n    \u0026\u0026 (firstProgrammer \u0026\u0026 secondProgrammer\n      ? \u003cp\u003e\u003cbold\u003e{firstProgrammer}\u003c/bold\u003e, you're going to do pair-programming with {secondProgrammer}.\u003c/p\u003e\n      : (firstProgrammer \u0026\u0026 !secondProgrammer\n        ? \u003cp\u003e\u003cbold\u003e{firstProgrammer}\u003c/bold\u003e, you'll code this task by yourself.\u003c/p\u003e\n        : \u003cp\u003eHey man! Can you tell us your name before we give you job to do?\u003c/p\u003e))\n\n    }\n  \u003c/div\u003e\n)\n```\n\n### HyperScript\n\nSimilarly to JSX, yet to the mix of unreadable ternary operators you're forced to add spread operator.\n\n```javascript\n({ \n  firstProgrammer,\n  secondProgrammer,\n  someCondition\n}) =\u003e h('div', {}, [\n  ...(someCondition ? [h('p', {}, [\n    ...(firstProgrammer \u0026\u0026 secondProgrammer ? [\n      h('strong', {}, [\n        firstProgrammer\n      ]),\n      `, you're going to do pair-programming with ${secondProgrammer}.`,\n    ] : []),\n    ...(firstProgrammer \u0026\u0026 !secondProgrammer ? [\n      h('strong', {}, [\n        firstProgrammer\n      ]),\n      ', you\\'ll code this task by yourself.',\n    ] : []),\n  ...(!firstProgrammer \u0026\u0026 !secondProgrammer ? [\n      'Hey man! Can you tell us your name before we give you job to do?',\n    ] : [])\n  ])] : [])\n])\n```\n\n### GlueDOM\n\nHere you can use block statements. When calling `text`, all its arguments are checked whether they are truthy and only if they are, they will be concatenated and rendered. \nThere is also a concept of formatters which are configured when initializing the top-most `tag`, and they can wrap texts inside a chosen tag and apply CSS classes on it. \nIn this case `bold` is configured to wrap props inside `\u003cstrong/\u003e` tag. \nNesting is possible by simply nesting objects e.g. `{ bold: { italic: 'some text' } }`.\n\n```javascript\n({ \n  firstProgrammer,\n  secondProgrammer,\n  someCondition\n}) =\u003e (\n  tag('div', (props, { tag }) =\u003e {\n    if (someCondition) {\n      tag('p', (props, { text }) =\u003e {\n        text({ bold: firstProgrammer }, ', you\\'re going to do pair-programming with ', secondProgrammer, '.')\n        \n        if (!secondProgrammer) {\n          text({ bold: { italic: firstProgrammer } }, ', you\\'ll code this task by yourself.')\n        }\n        \n        if (!firstProgrammer \u0026\u0026 !secondProgrammer) {\n          text('Hey man! Can you tell us your name before we give you job to do?')\n        }\n      })\n    }\n  })\n)\n```\n\n## Installation\n\nRun:\n```bash\nyarn add https://ide.glue.codes/repos/df67f7a82cbdc5efffcb31c519a48bf6/core/glueDom-1.0.1.tar.gz\n```\nOr:\n```bash\nnpm i https://ide.glue.codes/repos/df67f7a82cbdc5efffcb31c519a48bf6/core/glueDom-1.0.1.tar.gz --save\n```\n\n## Basic usage\n\n`renderer.js`:\n\n```javascript\nimport React from 'react'\nimport { createRenderer } from '@gluecodes/glueDom'\n\nexport default createRenderer({\n  createDomElement: React.createElement\n})\n```\n\n`component.js`:\n\n```javascript\nimport tag from './renderer'\n\nexport default () =\u003e tag('div', (props, { text }) =\u003e {\n  text('hello world!')\n}) \n```\n\n## Advanced Usage\n\n`renderer.js`:\n\n```javascript\nimport React from 'react'\nimport { createRenderer } from '@gluecodes/glueDom'\nimport styles from './textFormatters.css'\n\nexport default createRenderer({\n  createDomElement: React.createElement,\n  formatters: {\n    bold: () =\u003e ({\n      tag: 'strong',\n      props: { className: styles.bold }\n    }),\n    italic: () =\u003e ({\n      tag: 'span',\n      props: { className: styles.italic }\n    })\n  }\n})\n```\n\n`component.js`:\n\n```javascript\nimport tag from './renderer'\n\nexport default ({\n  email,\n  firstName,\n  interests,\n  surname\n}) =\u003e (\n  tag('p', (props, { text }) =\u003e {\n    text(\n      'You can format an email like this: \"', { bold: { italic: email } },\n      '\" and the whole sentence will appear only if email: ', email, ', firstName: ', firstName, ' and surname: ', surname, 'aren\\'t empty. ',\n      'It can be especially useful when generating documents from ', { bold: 'dynamic' }, ' fields coming from backend.'\n    )\n  })\n)\n```\n\n\t\n### GlueDOM Syntax \n\n### Rendering a tag\n\n**Nesting**\n\n```\ntag(tagName, (props, { component, tag, text }) =\u003e { \n  props.className = 'outerMostTag'\n  \n  tag(tagName, (props, { component, tag, text }) =\u003e { \n    props.className = 'innerTag'\n    \n    tag(tagName, (props, { component, tag, text }) =\u003e { \n      props.className = 'innerMostTag'\n      \n      ...      \n    })\n  })\n})\n```\n\n**Assigning props to an element containing child elements or text**\n\n```\ntag(tagName, (props, { text }) =\u003e {\n  props.className = 'someClass'\n  props.title = 'some tooltip'\n  \n  ...\n\n  text('some text')\n})\n```\n\n**No child elements nor text**\n\n```\ntag(tagName, {\n  [props]\n})\n```\n\n**No child elements nor props**\n```\ntag(tagName)\n```\n\n#### Inside Tag Callback\n\n**Nested Tag**\n\n```\ntag(tagName, ...)\n```\n\n**Components**\n\n```\ncomponent(reusableUiPiece, props)\n```\n\n**Text**\n\n```\ntext(...[textChunk,])\n```\n\n- `tagName` A string that specifies the type of element to be created \n- `props` An object to assign element props/attributes\n- `component` A function to render component\n- `tag` A function to create an element\n- `text` A function to create text\n- `reusableUiPiece` A function returning reusable DOM\n- `textChunk` Either a string or an object which uses text formatters. If any chunk is empty, the whole sentence won't be rendered\n\n## API\n\n```javascript\ncreateRenderer(config)\n```\n\n- `createRenderer()` A function to create initial `tag()` function based on provided `config`\n- `config` (optional) An object containing configuration\n  - `config.createDomElement` (optional) A function to create DOM element. \n  When specified, it should implement HyperScript-like interface/API   \n  - `config.formatters` An object of functions. They may be used in `text()` to wrap given string into `tag` with `props` of `config.formatters[formatterName]() =\u003e ({ tag, props })`\n    - `formatterName` A string identifying a formatter which may be used in `text()` like `text({ [formatterName]: 'given string' })`\n    - `tag` A string that specifies the type of element to be used for text wrapping\n    - `props` An object of props to be set on the wrapping element\n\n## Contributing\n\nFeel free to rise issues, open PRs or contact at hello@glue.codes about any ideas/criticism.\n\n## License\n\n[MIT](https://github.com/gluecodes/gluecodes-glue-dom/blob/master/LICENSE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgluecodes%2Fgluecodes-glue-dom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgluecodes%2Fgluecodes-glue-dom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgluecodes%2Fgluecodes-glue-dom/lists"}