├── .editorconfig ├── .eslintrc.cjs ├── .github ├── FUNDING.yaml └── workflows │ └── release.yaml ├── .gitignore ├── .prettierrc.yaml ├── .stylelintrc.cjs ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── custom-elements-manifest.config.js ├── lerna.json ├── package.json ├── packages ├── carbon │ ├── CHANGELOG.md │ ├── README.md │ ├── custom-elements.json │ ├── package.json │ ├── src │ │ ├── form.def.ts │ │ ├── form.ts │ │ ├── index.ts │ │ ├── styles.scss │ │ ├── styles.ts │ │ └── widgets │ │ │ ├── _all.scss │ │ │ ├── index.ts │ │ │ ├── number.ts │ │ │ ├── object.scss │ │ │ ├── object.ts │ │ │ ├── submit.scss │ │ │ ├── submit.ts │ │ │ ├── text.scss │ │ │ ├── text.ts │ │ │ └── textarea.ts │ └── tsconfig.json ├── form │ ├── CHANGELOG.md │ ├── README.md │ ├── custom-elements.json │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── json-schema-form.def.ts │ │ ├── json-schema-form.ts │ │ └── triage │ │ │ ├── array-primitive.ts │ │ │ ├── array.ts │ │ │ ├── object.ts │ │ │ └── primitive.ts │ └── tsconfig.json ├── material │ ├── CHANGELOG.md │ ├── README.md │ ├── custom-elements.json │ ├── package.json │ ├── src │ │ ├── form.def.ts │ │ ├── form.ts │ │ ├── index.ts │ │ ├── styles.scss │ │ ├── styles.ts │ │ └── widgets │ │ │ ├── _all.scss │ │ │ ├── _fieldset.scss │ │ │ ├── _toggle.scss │ │ │ ├── array.scss │ │ │ ├── array.ts │ │ │ ├── callout.scss │ │ │ ├── callout.ts │ │ │ ├── checkbox.ts │ │ │ ├── date.ts │ │ │ ├── index.ts │ │ │ ├── number.ts │ │ │ ├── object.scss │ │ │ ├── object.ts │ │ │ ├── range.scss │ │ │ ├── range.ts │ │ │ ├── select.ts │ │ │ ├── submit.scss │ │ │ ├── submit.ts │ │ │ ├── switch.scss │ │ │ ├── switch.ts │ │ │ ├── text.scss │ │ │ ├── text.ts │ │ │ └── textarea.ts │ └── tsconfig.json ├── shoelace │ ├── CHANGELOG.md │ ├── README.md │ ├── custom-elements.json │ ├── package.json │ ├── src │ │ ├── form.def.ts │ │ ├── form.ts │ │ ├── index.ts │ │ ├── styles.scss │ │ ├── styles.ts │ │ └── widgets │ │ │ ├── _all.scss │ │ │ ├── _fieldset.scss │ │ │ ├── array.scss │ │ │ ├── array.ts │ │ │ ├── button-group-boolean.ts │ │ │ ├── button-group.ts │ │ │ ├── callout.scss │ │ │ ├── callout.ts │ │ │ ├── checkbox-group.scss │ │ │ ├── checkbox-group.ts │ │ │ ├── checkbox.scss │ │ │ ├── checkbox.ts │ │ │ ├── color-picker.scss │ │ │ ├── color-picker.ts │ │ │ ├── date.ts │ │ │ ├── index.ts │ │ │ ├── number.ts │ │ │ ├── object.scss │ │ │ ├── object.ts │ │ │ ├── radio-group-boolean.scss │ │ │ ├── radio-group-boolean.ts │ │ │ ├── radio-group.scss │ │ │ ├── radio-group.ts │ │ │ ├── range.ts │ │ │ ├── rating.scss │ │ │ ├── rating.ts │ │ │ ├── select-multiple.ts │ │ │ ├── select.ts │ │ │ ├── submit.scss │ │ │ ├── submit.ts │ │ │ ├── switch.ts │ │ │ ├── text.ts │ │ │ └── textarea.ts │ └── tsconfig.json ├── spectrum │ ├── CHANGELOG.md │ ├── README.md │ ├── custom-elements.json │ ├── package.json │ ├── src │ │ ├── form.def.ts │ │ ├── form.ts │ │ ├── index.ts │ │ ├── styles.scss │ │ ├── styles.ts │ │ └── widgets │ │ │ ├── _fieldset.scss │ │ │ ├── index.ts │ │ │ ├── number.ts │ │ │ ├── object.scss │ │ │ ├── object.ts │ │ │ ├── radio-group.scss │ │ │ ├── radio-group.ts │ │ │ ├── range.ts │ │ │ ├── submit.scss │ │ │ ├── submit.ts │ │ │ ├── switch.ts │ │ │ ├── text.scss │ │ │ ├── text.ts │ │ │ └── textarea.ts │ └── tsconfig.json ├── system │ ├── CHANGELOG.md │ ├── README.md │ ├── custom-elements.json │ ├── package.json │ ├── src │ │ ├── form.def.ts │ │ ├── form.ts │ │ ├── index.ts │ │ ├── styles.scss │ │ ├── styles.ts │ │ └── widgets │ │ │ ├── _field.ts │ │ │ ├── callout.ts │ │ │ ├── checkbox.ts │ │ │ ├── color-picker.ts │ │ │ ├── date.ts │ │ │ ├── index.ts │ │ │ ├── number.ts │ │ │ ├── object.scss │ │ │ ├── object.ts │ │ │ ├── radio-group-boolean.ts │ │ │ ├── radio-group.ts │ │ │ ├── range.ts │ │ │ ├── select-multiple.ts │ │ │ ├── select.ts │ │ │ ├── submit.scss │ │ │ ├── submit.ts │ │ │ ├── text.ts │ │ │ └── textarea.ts │ └── tsconfig.json ├── types │ ├── CHANGELOG.md │ ├── README.md │ ├── custom-elements.json │ ├── package.json │ ├── src │ │ ├── form.ts │ │ ├── index.ts │ │ ├── ui-schema.ts │ │ └── widgets.ts │ └── tsconfig.json └── wired │ ├── CHANGELOG.md │ ├── README.md │ ├── custom-elements.json │ ├── package.json │ ├── src │ ├── form.def.ts │ ├── form.ts │ ├── index.ts │ ├── styles.scss │ ├── styles.ts │ └── widgets │ │ ├── _all.scss │ │ ├── _field.scss │ │ ├── _field.ts │ │ ├── index.ts │ │ ├── number.ts │ │ ├── object.scss │ │ ├── object.ts │ │ ├── range.scss │ │ ├── range.ts │ │ ├── submit.scss │ │ ├── submit.ts │ │ ├── text.scss │ │ ├── text.ts │ │ ├── textarea.scss │ │ └── textarea.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts ├── cem-to-md.js ├── css-to-js.js └── manifests.sh ├── tests ├── __fixtures__ │ ├── index.html │ └── prepare.ts ├── numbers.test.ts ├── package.json ├── playwright.config.ts └── vite.config.mjs ├── tsconfig.json └── turbo.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = tab 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = false 11 | insert_final_newline = true 12 | 13 | [**/Makefile] 14 | indent_style = tab 15 | indent_size = 4 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | 20 | [*.md,*.yaml,*.json] 21 | indent_style = space 22 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("@types/eslint").Linter.Config} */ 2 | 3 | const tsConfigs = [ 4 | './tsconfig.json', 5 | './packages/types/tsconfig.json', 6 | './packages/form/tsconfig.json', 7 | './packages/material/tsconfig.json', 8 | './packages/shoelace/tsconfig.json', 9 | './packages/wired/tsconfig.json', 10 | './packages/carbon/tsconfig.json', 11 | './packages/system/tsconfig.json', 12 | ]; 13 | 14 | module.exports = { 15 | settings: { 16 | // This loads /tsconfig.json to eslint 17 | 'import/resolver': { 18 | typescript: { 19 | project: tsConfigs, 20 | }, 21 | }, 22 | }, 23 | 24 | ignorePatterns: ['**/dist/**', '*.js', '*.cjs'], 25 | // env: { 26 | // node: true, 27 | // es2022: true, 28 | // browser: true, 29 | // }, 30 | 31 | overrides: [ 32 | { 33 | files: ['*.ts', '*.mts', '*.cts'], 34 | plugins: [ 35 | // 36 | 'eslint-plugin-tsdoc', 37 | '@typescript-eslint', 38 | ], 39 | 40 | parser: '@typescript-eslint/parser', 41 | parserOptions: { 42 | project: tsConfigs, // Specify it only for TypeScript files 43 | ecmaVersion: 'latest', 44 | sourceType: 'module', 45 | }, 46 | 47 | extends: [ 48 | 'airbnb-base', 49 | 'airbnb-typescript', 50 | 51 | 'plugin:@typescript-eslint/recommended', 52 | 'plugin:@typescript-eslint/recommended-requiring-type-checking', 53 | 'plugin:@typescript-eslint/strict', 54 | 55 | 'prettier', 56 | ], 57 | 58 | rules: { 59 | 'tsdoc/syntax': 'warn', 60 | 61 | '@typescript-eslint/no-unused-vars': [ 62 | 'error', 63 | { argsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_' }, 64 | ], 65 | '@typescript-eslint/no-non-null-assertion': 'off', 66 | 67 | 'max-lines': [ 68 | 'warn', 69 | { max: 150, skipComments: true, skipBlankLines: true }, 70 | ], 71 | 72 | 'react/jsx-filename-extension': 'off', 73 | 74 | 'import/prefer-default-export': 'off', 75 | 76 | 'import/extensions': 'off', 77 | 'prefer-destructuring': 'off', 78 | // 'import/extensions': [ 79 | // 'error', 80 | // 'ignorePackages', 81 | // { 82 | // js: 'never', 83 | // jsx: 'never', 84 | // ts: 'never', 85 | // tsx: 'never', 86 | // }, 87 | // ], 88 | 'import/order': 'off', 89 | 90 | 'arrow-body-style': 'off', 91 | }, 92 | }, 93 | ], 94 | }; 95 | -------------------------------------------------------------------------------- /.github/FUNDING.yaml: -------------------------------------------------------------------------------- 1 | github: [JulianCataldo] 2 | 3 | custom: ['https://www.buymeacoffee.com/JulianCataldo'] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .old* 2 | .dev* 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | pnpm-debug.log* 11 | lerna-debug.log* 12 | 13 | node_modules 14 | dist 15 | dist-ssr 16 | *.local 17 | 18 | # Editor directories and files 19 | .vscode/* 20 | !.vscode/extensions.json 21 | !.vscode/settings.json 22 | .idea 23 | .DS_Store 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | 30 | .design 31 | 32 | # Local Netlify folder 33 | .netlify 34 | 35 | .turbo* 36 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | useTabs: true 2 | singleQuote: true 3 | trailingComma: all 4 | 5 | overrides: 6 | - files: ['*.json', '*.yaml'] 7 | options: 8 | useTabs: false 9 | 10 | plugins: 11 | - '@trivago/prettier-plugin-sort-imports' 12 | 13 | # importOrder: ['^@core/(.*)$', '^@server/(.*)$', '^@ui/(.*)$', '^[./]'] 14 | # TODO: better custom ordering 15 | importOrder: ['^lit(.*)$', '^@jsfe/(.*)$', '^@(.*)$', '^[./]'] 16 | importOrderSeparation: true 17 | importOrderSortSpecifiers: true 18 | importOrderParserPlugins: ['typescript', 'decorators-legacy'] 19 | -------------------------------------------------------------------------------- /.stylelintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("@types/stylelint").Options} */ 2 | 3 | module.exports = { 4 | extends: [ 5 | 'stylelint-config-standard', 6 | 'stylelint-config-standard-scss', 7 | 'stylelint-config-prettier', 8 | 'stylelint-config-recess-order', 9 | ], 10 | 11 | plugins: [ 12 | // 13 | 'stylelint-order', 14 | ], 15 | 16 | overrides: [ 17 | { 18 | files: ['*.vue', '**/*.vue'], 19 | extends: [ 20 | 'stylelint-config-standard-scss', 21 | 'stylelint-config-prettier', 22 | 'stylelint-config-recommended-vue/scss', 23 | ], 24 | }, 25 | ], 26 | rules: { 27 | 'comment-empty-line-before': null, 28 | // Shorthand units make things hard to read and change 29 | 'shorthand-property-no-redundant-values': null, 30 | 'scss/double-slash-comment-empty-line-before': null, 31 | 'color-function-notation': null, 32 | 'property-no-vendor-prefix': null, 33 | 'value-keyword-case': null, 34 | 'declaration-block-no-redundant-longhand-properties': null, 35 | 'hue-degree-notation': null, 36 | 'length-zero-no-unit': null, 37 | 'alpha-value-notation': null, 38 | 'color-hex-length': null, 39 | 'scss/at-import-partial-extension': null, 40 | 'selector-pseudo-class-no-unknown': [ 41 | true, 42 | { 43 | ignorePseudoClasses: ['global'], 44 | }, 45 | ], 46 | 'selector-pseudo-element-no-unknown': [ 47 | true, 48 | { 49 | ignorePseudoElements: ['v-deep'], 50 | }, 51 | ], 52 | 'max-line-length': [100, , { ignore: ['comments'] }], 53 | 54 | 'selector-class-pattern': null, 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "stylelint.vscode-stylelint", 4 | "dbaeumer.vscode-eslint", 5 | "runem.lit-plugin", 6 | "esbenp.prettier-vscode" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cssVariables.lookupFiles": [ 3 | "**/*.css", 4 | "**/*.scss", 5 | "**/*.sass", 6 | "**/*.less", 7 | "packages/shoelace/node_modules/@shoelace-style/shoelace/dist/themes/dark.css", 8 | // "packages/shoelace/node_modules/@shoelace-style/shoelace/dist/themes/light.css", 9 | 10 | "packages/spectrum/node_modules/@spectrum-web-components/styles/spectrum-scale-large.css", 11 | "packages/spectrum/node_modules/@spectrum-web-components/styles/spectrum-theme-dark.css", 12 | "packages/spectrum/node_modules/@spectrum-web-components/styles/typography.css", 13 | "packages/spectrum/node_modules/@spectrum-web-components/styles/core-global.css", 14 | "packages/spectrum/node_modules/@spectrum-web-components/styles/spectrum-core-global.css" 15 | ], 16 | "markdown.extension.toc.levels": "2..6", 17 | 18 | // Unused 19 | "html.customData": ["./vscode.html-custom-data.json"], 20 | "css.customData": ["./vscode.css-custom-data.json"] 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2023 Julian Cataldo — https://www.juliancataldo.com 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 14 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /custom-elements-manifest.config.js: -------------------------------------------------------------------------------- 1 | // import { generateCustomData } from 'cem-plugin-vs-code-custom-data-generator'; 2 | 3 | export default { 4 | // FIXME: Adapt for mono-repo structure 5 | // plugins: [generateCustomData({ outdir: 'packages/form' })], 6 | }; 7 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/lerna/schemas/lerna-schema.json", 3 | "version": "independent", 4 | "npmClient": "pnpm", 5 | "command": { 6 | "publish": { 7 | "message": "chore: publish release" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/root", 3 | "version": "0.0.0", 4 | "private": true, 5 | "homepage": "https://jsfe.js.org", 6 | "description": "Effortless forms, with standards.", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/json-schema-form-element/jsfe" 10 | }, 11 | "license": "ISC", 12 | "author": { 13 | "name": "Julian Cataldo", 14 | "email": "contact@juliancataldo.com", 15 | "url": "https://www.juliancataldo.com" 16 | }, 17 | "type": "module", 18 | "scripts": { 19 | "dev": "turbo dev", 20 | "build": "turbo build", 21 | "gen:manifests": "./scripts/manifests.sh", 22 | "// test": "cd tests && pnpm test", 23 | "// publish:dry": "pnpm lerna publish --dry-run", 24 | "util:scaffold": "plop --plopfile scripts/scaffold/plopfile.js", 25 | "util:sort": "keep-sorted packages/*/src/widgets/index.ts packages/*/src/styles.scss && sort-package-json packages/*/package.json" 26 | }, 27 | "devDependencies": { 28 | "@custom-elements-manifest/analyzer": "^0.9.0", 29 | "@trivago/prettier-plugin-sort-imports": "^4.3.0", 30 | "@types/eslint": "^8.44.8", 31 | "@types/node": "^20.10.1", 32 | "@typescript-eslint/eslint-plugin": "^6.13.1", 33 | "@typescript-eslint/parser": "^6.13.1", 34 | "cem-plugin-vs-code-custom-data-generator": "^1.4.2", 35 | "esbuild": "^0.19.8", 36 | "eslint": "^8.54.0", 37 | "eslint-config-airbnb-base": "^15.0.0", 38 | "eslint-config-airbnb-typescript": "^17.1.0", 39 | "eslint-config-prettier": "^9.0.0", 40 | "eslint-import-resolver-typescript": "^3.6.1", 41 | "eslint-plugin-import": "^2.29.0", 42 | "eslint-plugin-lit": "^1.10.1", 43 | "eslint-plugin-lit-a11y": "^4.1.1", 44 | "eslint-plugin-prettier": "^5.0.1", 45 | "eslint-plugin-tsdoc": "^0.2.17", 46 | "lerna": "^8.0.0", 47 | "prettier": "^3.1.0", 48 | "remark": "^15.0.1", 49 | "sort-package-json": "^2.6.0", 50 | "stylelint": "^15.11.0", 51 | "stylelint-config-prettier": "^9.0.5", 52 | "stylelint-config-recess-order": "^4.4.0", 53 | "stylelint-config-recommended": "^13.0.0", 54 | "stylelint-config-recommended-scss": "^13.1.0", 55 | "stylelint-config-standard": "^34.0.0", 56 | "stylelint-config-standard-scss": "^11.1.0", 57 | "stylelint-order": "^6.0.3", 58 | "tsx": "^4.6.1", 59 | "turbo": "^1.10.16", 60 | "typedoc": "^0.25.4", 61 | "typedoc-plugin-markdown": "^3.17.1", 62 | "typescript": "^5.3.2" 63 | }, 64 | "dependencies": { 65 | "@custom-elements-manifest/to-markdown": "^0.1.0", 66 | "plop": "^4.0.0" 67 | }, 68 | "packageManager": "pnpm@9.0.6" 69 | } 70 | -------------------------------------------------------------------------------- /packages/carbon/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.2.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/carbon@0.1.4...@jsfe/carbon@0.2.0) (2024-06-06) 7 | 8 | ### Bug Fixes 9 | 10 | - add types field to package.json ([#8](https://github.com/json-schema-form-element/jsfe/issues/8)) ([e0ecf92](https://github.com/json-schema-form-element/jsfe/commit/e0ecf923475ca123ea9ee76d331d35d68c591102)) 11 | 12 | ### Features 13 | 14 | - submit button custom text ([e751828](https://github.com/json-schema-form-element/jsfe/commit/e751828b3bc07d21fc537b97e1b531a94a77fd70)), closes [#7](https://github.com/json-schema-form-element/jsfe/issues/7) 15 | 16 | ### Reverts 17 | 18 | - "style: bulk reformat with new imports orders" ([1236078](https://github.com/json-schema-form-element/jsfe/commit/12360786626927f2c8a0273b245b906e5946b920)) 19 | 20 | ## [0.1.4](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/carbon@0.1.3...@jsfe/carbon@0.1.4) (2023-11-30) 21 | 22 | **Note:** Version bump only for package @jsfe/carbon 23 | 24 | ## [0.1.3](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/carbon@0.1.2...@jsfe/carbon@0.1.3) (2023-11-30) 25 | 26 | **Note:** Version bump only for package @jsfe/carbon 27 | 28 | ## [0.1.2](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/carbon@0.1.1...@jsfe/carbon@0.1.2) (2023-11-30) 29 | 30 | **Note:** Version bump only for package @jsfe/carbon 31 | 32 | ## [0.1.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/carbon@0.1.0...@jsfe/carbon@0.1.1) (2023-10-12) 33 | 34 | ### Bug Fixes 35 | 36 | - better styles, material export ([9a33cbb](https://github.com/json-schema-form-element/jsfe/commit/9a33cbb29059ac8827647db6a7deda45d9cb3c09)) 37 | 38 | # 0.1.0 (2023-10-11) 39 | 40 | ### Features 41 | 42 | - add base packages files ([d8e42bd](https://github.com/json-schema-form-element/jsfe/commit/d8e42bdcda5f8af5e2728e1556946d333e7f59b5)) 43 | - add components for all ui libraries packages ([7a6d3a5](https://github.com/json-schema-form-element/jsfe/commit/7a6d3a53f3939d00512c9f42925d1f9f1db246ff)) 44 | - re-organize to mono-repo ([3888c62](https://github.com/json-schema-form-element/jsfe/commit/3888c62a07b07aed2262c7e0c7b66919f30505ef)) 45 | - select multiple, typings fixes, refactor ([463aa85](https://github.com/json-schema-form-element/jsfe/commit/463aa85d7ba22480513bc485ab4ad849e39c5402)) 46 | -------------------------------------------------------------------------------- /packages/carbon/README.md: -------------------------------------------------------------------------------- 1 | # JSON Schema Form Element — ***Carbon*** edition 2 | 3 | ```sh 4 | npm install @jsfe/carbon 5 | ``` 6 | 7 | - Consult the [documentation](../../README.md). 8 | - Open the [playground](https://jsfe.js.org). 9 | - Try the [examples](https://github.com/json-schema-form-element/examples#readme). 10 | 11 | --- 12 | 13 | # `packages/carbon/src/form.def.ts`: 14 | 15 | ## Exports 16 | 17 | | Kind | Name | Declaration | Module | Package | 18 | | --------------------------- | ------------ | ----------- | ---------------------------- | ------- | 19 | | `custom-element-definition` | `jsf-carbon` | JsfCarbon | /packages/carbon/src/form.js | | 20 | 21 | # `packages/carbon/src/form.ts`: 22 | 23 | ## class: `JsfCarbon`, `jsf-carbon` 24 | 25 | ### Superclass 26 | 27 | | Name | Module | Package | 28 | | ----- | ------ | ---------- | 29 | | `Jsf` | | @jsfe/form | 30 | 31 | ### Fields 32 | 33 | | Name | Privacy | Type | Default | Description | Inherited From | 34 | | ------------- | ------- | ------- | ---------- | ----------- | -------------- | 35 | | `widgets` | public | | `widgets` | | | 36 | | `styleSheets` | public | `array` | `[styles]` | | | 37 | 38 |
39 | 40 | ## Exports 41 | 42 | | Kind | Name | Declaration | Module | Package | 43 | | ---- | ----------- | ----------- | --------------------------- | ------- | 44 | | `js` | `JsfCarbon` | JsfCarbon | packages/carbon/src/form.ts | | 45 | 46 | # `packages/carbon/src/index.ts`: 47 | 48 | ## Exports 49 | 50 | | Kind | Name | Declaration | Module | Package | 51 | | ---- | ----------- | ----------- | --------- | ------------------ | 52 | | `js` | `JsfCarbon` | JsfCarbon | ./form.js | | 53 | | `js` | `*` | \* | | ./widgets/index.js | 54 | | `js` | `*` | \* | | @jsfe/types | 55 | | `js` | `Jsf` | Jsf | | @jsfe/form | 56 | 57 | # `packages/carbon/src/styles.ts`: 58 | 59 | ## Variables 60 | 61 | | Name | Description | Type | 62 | | -------- | ----------- | ---- | 63 | | `styles` | | | 64 | 65 |
66 | 67 | ## Exports 68 | 69 | | Kind | Name | Declaration | Module | Package | 70 | | ---- | -------- | ----------- | ----------------------------- | ------- | 71 | | `js` | `styles` | styles | packages/carbon/src/styles.ts | | 72 | 73 | -------------------------------------------------------------------------------- /packages/carbon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/carbon", 3 | "version": "0.2.0", 4 | "description": "IBM Carbon Components v2 forms, auto-generated by JSON schemas.", 5 | "keywords": [ 6 | "json-schema", 7 | "carbon", 8 | "ibm", 9 | "forms", 10 | "generation", 11 | "declarative", 12 | "openapi", 13 | "mongodb", 14 | "page-builder" 15 | ], 16 | "homepage": "https://jsfe.js.org", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/json-schema-form-element/jsfe", 20 | "directory": "packages/carbon" 21 | }, 22 | "license": "ISC", 23 | "author": { 24 | "name": "Julian Cataldo", 25 | "email": "contact@juliancataldo.com", 26 | "url": "https://www.juliancataldo.com" 27 | }, 28 | "type": "module", 29 | "types": "./dist/esm/index.d.ts", 30 | "exports": { 31 | ".": "./dist/esm/index.js", 32 | "./scss": "./src/styles.scss", 33 | "./scss/*": "./src/widgets/*.scss", 34 | "./css": "./dist/esm/styles.css", 35 | "./jss": "./dist/esm/styles.js", 36 | "./min": "./dist/esm-min" 37 | }, 38 | "files": [ 39 | "./dist/esm", 40 | "./dist/esm-min", 41 | "./src/**/*.scss", 42 | "./vscode.html-custom-data.json", 43 | "./vscode.css-custom-data.json", 44 | "./custom-elements.json" 45 | ], 46 | "scripts": { 47 | "build": "pnpm clean ; pnpm ts:build ; pnpm css:build ; pnpm css:to-js", 48 | "clean": "rm -rf ./dist", 49 | "css:build": "pnpm sass --no-source-map src/styles.scss:dist/esm/styles.css", 50 | "css:dev": "pnpm sass --watch src/styles.scss:dist/esm/styles.css & pnpm css:to-js:dev", 51 | "css:to-js": "node ../../scripts/css-to-js.js dist/esm/styles.css", 52 | "css:to-js:dev": "nodemon dist/esm/styles.css -x 'pnpm css:to-js'", 53 | "dev": "pnpm ts:dev & pnpm css:dev & (sleep 3 && pnpm css:to-js:dev)", 54 | "ts:build": "pnpm tsc", 55 | "ts:dev": "pnpm tsc --watch" 56 | }, 57 | "dependencies": { 58 | "@jsfe/form": "workspace:*", 59 | "@jsfe/types": "workspace:*" 60 | }, 61 | "devDependencies": { 62 | "sass": "^1.69.5", 63 | "typescript": "^5.3.2" 64 | }, 65 | "peerDependencies": { 66 | "@carbon/styles": "^1.39.0", 67 | "@carbon/web-components": "^2.0.1", 68 | "lit": "^3.1.0" 69 | }, 70 | "publishConfig": { 71 | "access": "public" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/carbon/src/form.def.ts: -------------------------------------------------------------------------------- 1 | import { JsfCarbon } from './form.js'; 2 | 3 | customElements.define('jsf-carbon', JsfCarbon); 4 | 5 | declare global { 6 | interface HTMLElementTagNameMap { 7 | 'jsf-carbon': JsfCarbon; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/carbon/src/form.ts: -------------------------------------------------------------------------------- 1 | import { Jsf } from '@jsfe/form'; 2 | import * as widgets from './widgets/index.js'; 3 | import { styles } from './styles.js'; 4 | 5 | export class JsfCarbon extends Jsf { 6 | public widgets = widgets; 7 | 8 | public styleSheets = [styles]; 9 | } 10 | -------------------------------------------------------------------------------- /packages/carbon/src/index.ts: -------------------------------------------------------------------------------- 1 | import './form.def.js'; 2 | 3 | export { JsfCarbon } from './form.js'; 4 | export * as widgets from './widgets/index.js'; 5 | 6 | export type * from '@jsfe/types'; 7 | export type { Jsf } from '@jsfe/form'; 8 | -------------------------------------------------------------------------------- /packages/carbon/src/styles.scss: -------------------------------------------------------------------------------- 1 | // keep-sorted start 2 | @import './widgets/_all.scss'; 3 | @import './widgets/object.scss'; 4 | @import './widgets/submit.scss'; 5 | @import './widgets/text.scss'; 6 | // keep-sorted end 7 | -------------------------------------------------------------------------------- /packages/carbon/src/styles.ts: -------------------------------------------------------------------------------- 1 | /* STUB */ 2 | 3 | import { css } from 'lit'; 4 | 5 | export const styles = css` 6 | /* STUB - Compiled SCSS goes here */ 7 | `; 8 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/_all.scss: -------------------------------------------------------------------------------- 1 | .theme-carbon { 2 | font-family: var(--cds-font-sans); 3 | font-size: 16px; 4 | } 5 | 6 | // @use '@carbon/styles/scss/reset'; 7 | // @use '@carbon/styles/scss/theme'; 8 | // @use '@carbon/styles/scss/themes'; 9 | 10 | // :root { 11 | // @include theme.theme(themes.$white); 12 | // background-color: var(--cds-background); 13 | // color: var(--cds-text-primary); 14 | // } 15 | 16 | // @use '@carbon/styles/scss/reset'; 17 | // @use '@carbon/styles/scss/theme'; 18 | // @use '@carbon/styles/scss/themes'; 19 | // @use '@carbon/styles/scss/fonts'; 20 | 21 | // .theme-carbon { 22 | // --cds-font-sans: 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif; 23 | 24 | // @include theme.theme(themes.$g100); 25 | // background-color: var(--cds-background); 26 | // color: var(--cds-text-primary); 27 | // } 28 | 29 | // background-color: var(--cds-background); 30 | // color: var(--cds-text-primary); 31 | 32 | // @use '@carbon/styles/scss/reset'; 33 | // @use '@carbon/styles/scss/theme'; 34 | // @use '@carbon/styles/scss/themes'; 35 | // // @use '@carbon/styles/scss/fonts'; 36 | 37 | // // @import '@carbon/type/scss/type'; 38 | // // @import '@carbon/type/scss/font-face/mono'; 39 | // // @import '@carbon/type/scss/font-face/sans'; 40 | 41 | // // @include carbon--type-reset(); 42 | // // @include carbon--font-face-mono(); 43 | // // @include carbon--font-face-sans(); 44 | 45 | // @import '@carbon/type/scss/font-face/sans'; 46 | 47 | // @include carbon--font-face-sans(); 48 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/index.ts: -------------------------------------------------------------------------------- 1 | // keep-sorted start 2 | export { number } from './number.js'; 3 | export { object } from './object.js'; 4 | export { submit } from './submit.js'; 5 | export { text } from './text.js'; 6 | export { textarea } from './textarea.js'; 7 | // keep-sorted end 8 | 9 | // export { checkbox } from './widgets/checkbox.js'; 10 | // export { range } from './widgets/range.js'; 11 | // export { enumeration } from './widgets/enumeration.js'; 12 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/number.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@carbon/web-components/es/components/number-input/index.js'; 7 | import type CDSTextInput from '@carbon/web-components/es/components/number-input/number-input.js'; 8 | 9 | export const number: Widgets['number'] = (options) => html` 10 | { 22 | const { valueAsNumber: newValue } = event.target as CDSTextInput; 23 | options.valueChangedCallback?.(newValue); 24 | }} 25 | @keydown=${options.handleKeydown} 26 | > 27 | 32 | 33 | `; 34 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/object.scss: -------------------------------------------------------------------------------- 1 | .theme-carbon.widget-object { 2 | .widget-object__children { 3 | position: relative; 4 | display: flex; 5 | flex-direction: column; 6 | gap: 2em 0; 7 | padding: 1.5em 1em; 8 | } 9 | // margin: 0; 10 | // font-weight: 300; 11 | // border: none; 12 | // // border: 1px solid transparent; 13 | // border-radius: 1rem; 14 | // transition-timing-function: ease-in-out; 15 | // transition-duration: 250ms; 16 | 17 | // legend { 18 | // width: 100%; 19 | // font-size: 1.5em; 20 | // border-bottom: 1px solid ///; 21 | // } 22 | 23 | .widget-object__description { 24 | margin: 1rem 0 0rem 0; 25 | opacity: calc(3 / 4); 26 | font-size: var(--cds-label-02-font-size); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/object.ts: -------------------------------------------------------------------------------- 1 | import { nothing, html } from 'lit'; 2 | import type { Widgets } from '@jsfe/types'; 3 | 4 | import '@carbon/web-components/es/components/form-group/index.js'; 5 | import '@carbon/web-components/es/components/stack/index.js'; 6 | 7 | export const object: Widgets['object'] = (options) => html` 8 | 14 | 16 | 17 | ${options.helpText 18 | ? html`
${options.helpText}
` 19 | : nothing} 20 |
${options.children}
21 |
22 | `; 23 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/submit.scss: -------------------------------------------------------------------------------- 1 | .theme-carbon.widget-submit { 2 | display: flex; 3 | justify-content: center; 4 | margin: 2.5rem 0; 5 | font-size: 4em; 6 | } 7 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/submit.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@carbon/web-components/es/components/button/index.js'; 6 | 7 | export const submit: Widgets['submit'] = (options) => html` 8 | 9 |
10 | ${options.label ?? 'Submit'} 13 |
14 | `; 15 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/text.scss: -------------------------------------------------------------------------------- 1 | .theme-carbon.widget-text { 2 | // 3 | } 4 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/text.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@carbon/web-components/es/components/text-input/index.js'; 7 | import type CDSTextInput from '@carbon/web-components/es/components/text-input/text-input.js'; 8 | 9 | export const text: Widgets['text'] = (options) => html` 10 | { 25 | const { value: newValue } = event.target as CDSTextInput; 26 | options.valueChangedCallback?.(newValue); 27 | }} 28 | @keydown=${options.handleKeydown} 29 | > 30 | 31 | `; 32 | -------------------------------------------------------------------------------- /packages/carbon/src/widgets/textarea.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@carbon/web-components/es/components/textarea/index.js'; 7 | import type CDSTextArea from '@carbon/web-components/es/components/textarea/textarea.js'; 8 | 9 | export const textarea: Widgets['text'] = (options) => html` 10 | { 24 | const { value: newValue } = event.target as CDSTextArea; 25 | options.valueChangedCallback?.(newValue); 26 | }} 27 | @keydown=${options.handleKeydown} 28 | > 29 | 30 | `; 31 | -------------------------------------------------------------------------------- /packages/carbon/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "Bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "outDir": "dist/esm", 22 | "declaration": true, 23 | 24 | "removeComments": true, 25 | 26 | "noImplicitAny": true, 27 | 28 | "declarationMap": true 29 | }, 30 | "include": [ 31 | // 32 | "src/index.ts" 33 | ], 34 | "exclude": ["src/styles.ts"] 35 | } 36 | -------------------------------------------------------------------------------- /packages/form/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.4.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/form@0.3.1...@jsfe/form@0.4.0) (2024-06-06) 7 | 8 | ### Bug Fixes 9 | 10 | - add types field to package.json ([#8](https://github.com/json-schema-form-element/jsfe/issues/8)) ([e0ecf92](https://github.com/json-schema-form-element/jsfe/commit/e0ecf923475ca123ea9ee76d331d35d68c591102)) 11 | 12 | ### Features 13 | 14 | - submit button custom text ([e751828](https://github.com/json-schema-form-element/jsfe/commit/e751828b3bc07d21fc537b97e1b531a94a77fd70)), closes [#7](https://github.com/json-schema-form-element/jsfe/issues/7) 15 | 16 | ### Reverts 17 | 18 | - "style: bulk reformat with new imports orders" ([1236078](https://github.com/json-schema-form-element/jsfe/commit/12360786626927f2c8a0273b245b906e5946b920)) 19 | 20 | ## [0.3.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/form@0.3.0...@jsfe/form@0.3.1) (2023-11-30) 21 | 22 | **Note:** Version bump only for package @jsfe/form 23 | 24 | # [0.3.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/form@0.2.0...@jsfe/form@0.3.0) (2023-11-30) 25 | 26 | ### Features 27 | 28 | - "disabled" and "read only" ui schema options ([01952e2](https://github.com/json-schema-form-element/jsfe/commit/01952e23355ffd5ce3cd13f8a8e5de4b05cc852c)) 29 | 30 | # [0.2.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/form@0.1.1...@jsfe/form@0.2.0) (2023-11-30) 31 | 32 | ### Features 33 | 34 | - ui schema title + desc., better date formats ([208e596](https://github.com/json-schema-form-element/jsfe/commit/208e59600d2a6811d0596572f1bc1ea4f240a945)) 35 | 36 | ## [0.1.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/form@0.1.0...@jsfe/form@0.1.1) (2023-10-12) 37 | 38 | ### Bug Fixes 39 | 40 | - better styles, material export ([9a33cbb](https://github.com/json-schema-form-element/jsfe/commit/9a33cbb29059ac8827647db6a7deda45d9cb3c09)) 41 | 42 | # 0.1.0 (2023-10-11) 43 | 44 | ### Features 45 | 46 | - add base packages files ([d8e42bd](https://github.com/json-schema-form-element/jsfe/commit/d8e42bdcda5f8af5e2728e1556946d333e7f59b5)) 47 | - extract widgets from core to packages ([fc247f6](https://github.com/json-schema-form-element/jsfe/commit/fc247f6475567deefde803ad5aedd35f3b9e70d7)) 48 | - re-organize to mono-repo ([3888c62](https://github.com/json-schema-form-element/jsfe/commit/3888c62a07b07aed2262c7e0c7b66919f30505ef)) 49 | - select multiple, typings fixes, refactor ([463aa85](https://github.com/json-schema-form-element/jsfe/commit/463aa85d7ba22480513bc485ab4ad849e39c5402)) 50 | -------------------------------------------------------------------------------- /packages/form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/form", 3 | "version": "0.4.0", 4 | "description": "Effortless forms, with standards. Base form element for all implementations.", 5 | "keywords": [ 6 | "json-schema", 7 | "forms", 8 | "generation", 9 | "declarative", 10 | "openapi", 11 | "mongodb", 12 | "page-builder" 13 | ], 14 | "homepage": "https://jsfe.js.org", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/json-schema-form-element/jsfe", 18 | "directory": "packages/form" 19 | }, 20 | "license": "ISC", 21 | "author": { 22 | "name": "Julian Cataldo", 23 | "email": "contact@juliancataldo.com", 24 | "url": "https://www.juliancataldo.com" 25 | }, 26 | "type": "module", 27 | "types": "./dist/esm/index.d.ts", 28 | "exports": { 29 | ".": "./dist/esm/index.js", 30 | "./scss": "./src/styles.scss", 31 | "./scss/*": "./src/widgets/*.scss", 32 | "./css": "./dist/esm/styles.css", 33 | "./jss": "./dist/esm/styles.js", 34 | "./min": "./dist/esm-min" 35 | }, 36 | "files": [ 37 | "./dist/esm", 38 | "./dist/esm-min", 39 | "./src/**/*.scss", 40 | "./vscode.html-custom-data.json", 41 | "./vscode.css-custom-data.json", 42 | "./custom-elements.json" 43 | ], 44 | "scripts": { 45 | "// build": "pnpm clean ; pnpm ts:build ; pnpm css:build ; pnpm css:to-js", 46 | "// build:cdn": "pnpm clean ; pnpm esbuild './lib/**/*.ts' --outdir=./dist/esm-min --outbase=./lib --minify && cp dist/esm/styles.js dist/esm-min", 47 | "// css:build": "pnpm sass --no-source-map src/styles.scss:dist/esm/styles.css", 48 | "// css:dev": "pnpm sass --watch src/styles.scss:dist/esm/styles.css & pnpm css:to-js:dev", 49 | "// css:to-js": "node ../../scripts/css-to-js.js dist/esm/styles.css", 50 | "// css:to-js:dev": "nodemon dist/esm/styles.css -x 'pnpm css:to-js'", 51 | "// dev": "pnpm ts:dev & pnpm css:dev & (sleep 3 && pnpm css:to-js:dev)", 52 | "build": "pnpm clean ; pnpm ts:build || exit 0", 53 | "clean": "rm -rf ./dist", 54 | "dev": "pnpm ts:dev", 55 | "ts:build": "pnpm tsc", 56 | "ts:dev": "pnpm tsc --watch" 57 | }, 58 | "dependencies": { 59 | "@jsfe/types": "workspace:*", 60 | "lodash-es": "^4.17.21" 61 | }, 62 | "devDependencies": { 63 | "@types/lodash-es": "^4.17.12", 64 | "sass": "^1.69.5", 65 | "typescript": "^5.3.2" 66 | }, 67 | "peerDependencies": { 68 | "lit": "^3.1.0" 69 | }, 70 | "publishConfig": { 71 | "access": "public" 72 | }, 73 | "customElements": "custom-elements.json" 74 | } 75 | -------------------------------------------------------------------------------- /packages/form/src/index.ts: -------------------------------------------------------------------------------- 1 | import './json-schema-form.def.js'; 2 | 3 | export { Jsf } from './json-schema-form.js'; 4 | 5 | export type * from '@jsfe/types'; 6 | export type { Jsf } from '@jsfe/form'; 7 | -------------------------------------------------------------------------------- /packages/form/src/json-schema-form.def.ts: -------------------------------------------------------------------------------- 1 | import { Jsf } from './json-schema-form.js'; 2 | 3 | customElements.define('json-schema-form', Jsf); 4 | 5 | declare global { 6 | interface HTMLElementTagNameMap { 7 | 'json-schema-form': Jsf; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/form/src/triage/array-primitive.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Jsf } from '../json-schema-form.js'; 4 | import type { Widgets, Path, UiSchema, JSONSchema7 } from '@jsfe/types'; 5 | 6 | export const fieldArrayPrimitive = ( 7 | schema: JSONSchema7, 8 | dataLevel: unknown, 9 | path: Path, 10 | uiState: unknown, 11 | uiOptions: UiSchema, 12 | required: boolean, 13 | handleChange: Jsf['_handleChange'], 14 | // dig: Jsf['_dig'], 15 | schemaPath: Path, 16 | widgets: Widgets, 17 | level = 0, 18 | ) => { 19 | // if (!Array.isArray(dataLevel)) return html``; 20 | 21 | const id = path.join('.'); 22 | function missing(widgetName: string) { 23 | const options = { id, message: `Missing ${widgetName} widget.` }; 24 | return widgets?.callout?.(options) ?? html`

${options.message}

`; 25 | } 26 | 27 | const helpText = 28 | schema.description ?? (uiOptions?.['ui:help'] as string) ?? ''; 29 | 30 | // NOTE: Unused for now. Too noisy? 31 | // let itemsLabel: string | undefined; 32 | // if ( 33 | // typeof schema.items === 'object' && 34 | // !Array.isArray(schema.items) && 35 | // schema.items.title 36 | // ) { 37 | // itemsLabel = schema.items.title; 38 | // } 39 | 40 | // options.value ||= []; 41 | // if (!Array.isArray(options.value)) return; 42 | 43 | const items = schema.items; 44 | if (typeof items !== 'object' || Array.isArray(items)) return; 45 | 46 | const valueChangedCallback = (enumValue: (string | number)[]) => { 47 | const schemaPathAugmented = [ 48 | ...schemaPath, 49 | 'items', 50 | 'enum', 51 | // FIXME: 52 | // enumValue.at(-1), 53 | ]; 54 | handleChange(path, enumValue, schemaPathAugmented); 55 | }; 56 | 57 | const disabled = uiOptions?.['ui:disabled'] || false; 58 | 59 | const options = { 60 | label: schema.title, 61 | helpText, 62 | 63 | value: dataLevel ?? schema?.default, 64 | enum: items.enum, 65 | 66 | disabled, 67 | 68 | // itemsLabel, 69 | 70 | level, 71 | 72 | id, 73 | // required, 74 | valueChangedCallback, 75 | }; 76 | 77 | if (uiOptions?.['ui:widget'] === 'select') { 78 | return widgets?.selectMultiple?.(options) || missing('multi select'); 79 | } 80 | 81 | return widgets?.checkboxGroup?.(options) || missing('array primitive'); 82 | }; 83 | -------------------------------------------------------------------------------- /packages/form/src/triage/object.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import type { JSONSchema7 } from '@jsfe/types'; 3 | import type { Widgets, Jsf, Path, UiSchema } from '../index.js'; 4 | 5 | export const fieldObject = ( 6 | schema: JSONSchema7, 7 | data: unknown, 8 | path: Path, 9 | uiState: unknown, 10 | uiSchema: UiSchema, 11 | dig: Jsf['_dig'], 12 | schemaPath: Path, 13 | widgets: Widgets, 14 | level = 0, 15 | ) => { 16 | const error = 'Wrong object field'; 17 | if (typeof schema.properties !== 'object') 18 | return widgets.callout?.({ id: '', message: error }) ?? html`${error}`; 19 | 20 | const children = Object.entries(schema.properties).map( 21 | ([propName, propValue]) => { 22 | if (Array.isArray(propValue) || typeof propValue === 'boolean') 23 | return html``; 24 | 25 | const value = data?.[propName] as unknown; 26 | const subPath = [...path, propName]; 27 | 28 | const required = schema.required?.includes(propName); 29 | 30 | const schemaPathAugmented = [...schemaPath]; 31 | // const propName = path.at(-1); 32 | // schemaPathAugmented.push(propName); 33 | // if (!Number.isNaN(propName)) { 34 | // schemaPathAugmented.push(Number(propName)); 35 | // } else { 36 | // } 37 | schemaPathAugmented.push(propName); 38 | 39 | // ${debuggerInline({ schemaPath, path })} 40 | return dig( 41 | propValue, 42 | value, 43 | subPath, 44 | uiState?.[propName], 45 | uiSchema?.[propName], 46 | schemaPathAugmented, 47 | required, 48 | level + 1, 49 | ); 50 | }, 51 | ); 52 | let label: string | undefined; 53 | const key = path.at(-1); 54 | if (schema.title) { 55 | label = schema.title; 56 | } else if (typeof key !== 'number') { 57 | label = key; 58 | } 59 | if (typeof uiSchema?.['ui:title'] === 'string') label = uiSchema['ui:title']; 60 | 61 | const options = { 62 | id: path.join('.'), 63 | label, 64 | helpText: schema.description, 65 | children, 66 | level, 67 | }; 68 | 69 | return ( 70 | widgets?.object?.(options) ?? 71 | widgets?.callout?.({ id: '', message: error }) ?? 72 | html`${error}` 73 | ); 74 | }; 75 | 76 | // ${ 77 | // /* NOTE: Experimental */ schema?.additionalProperties 78 | // ? html`
79 | // ${schema?.additionalProperties 80 | // ? dig( 81 | // { 82 | // type: 'array', 83 | // items: { 84 | // type: 'string', 85 | // }, 86 | // }, 87 | // data, 88 | // [...path], 89 | // uiState, 90 | // uiSchema, 91 | // schemaPath, 92 | // ) 93 | // : nothing} 94 | //
` 95 | // : nothing 96 | // } 97 | -------------------------------------------------------------------------------- /packages/form/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "outDir": "dist/esm", 22 | "declaration": true, 23 | 24 | "removeComments": true, 25 | 26 | "noImplicitAny": true, 27 | 28 | "declarationMap": true 29 | }, 30 | "include": [ 31 | // 32 | "src/index.ts" 33 | ], 34 | "exclude": ["src/styles.ts"] 35 | } 36 | -------------------------------------------------------------------------------- /packages/material/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.3.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/material@0.2.2...@jsfe/material@0.3.0) (2024-06-06) 7 | 8 | ### Bug Fixes 9 | 10 | - add types field to package.json ([#8](https://github.com/json-schema-form-element/jsfe/issues/8)) ([e0ecf92](https://github.com/json-schema-form-element/jsfe/commit/e0ecf923475ca123ea9ee76d331d35d68c591102)) 11 | 12 | ### Features 13 | 14 | - submit button custom text ([e751828](https://github.com/json-schema-form-element/jsfe/commit/e751828b3bc07d21fc537b97e1b531a94a77fd70)), closes [#7](https://github.com/json-schema-form-element/jsfe/issues/7) 15 | 16 | ### Reverts 17 | 18 | - "style: bulk reformat with new imports orders" ([1236078](https://github.com/json-schema-form-element/jsfe/commit/12360786626927f2c8a0273b245b906e5946b920)) 19 | 20 | ## [0.2.2](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/material@0.2.1...@jsfe/material@0.2.2) (2023-11-30) 21 | 22 | **Note:** Version bump only for package @jsfe/material 23 | 24 | ## [0.2.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/material@0.2.0...@jsfe/material@0.2.1) (2023-11-30) 25 | 26 | **Note:** Version bump only for package @jsfe/material 27 | 28 | # [0.2.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/material@0.1.1...@jsfe/material@0.2.0) (2023-11-30) 29 | 30 | ### Features 31 | 32 | - more system / wired widgets, clean others ([bfcac92](https://github.com/json-schema-form-element/jsfe/commit/bfcac9247ded39af312b2df99a21a6d94d37c965)) 33 | 34 | ## [0.1.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/material@0.1.0...@jsfe/material@0.1.1) (2023-10-12) 35 | 36 | ### Bug Fixes 37 | 38 | - better styles, material export ([9a33cbb](https://github.com/json-schema-form-element/jsfe/commit/9a33cbb29059ac8827647db6a7deda45d9cb3c09)) 39 | 40 | # 0.1.0 (2023-10-11) 41 | 42 | ### Features 43 | 44 | - add base packages files ([d8e42bd](https://github.com/json-schema-form-element/jsfe/commit/d8e42bdcda5f8af5e2728e1556946d333e7f59b5)) 45 | - add components for all ui libraries packages ([7a6d3a5](https://github.com/json-schema-form-element/jsfe/commit/7a6d3a53f3939d00512c9f42925d1f9f1db246ff)) 46 | - re-organize to mono-repo ([3888c62](https://github.com/json-schema-form-element/jsfe/commit/3888c62a07b07aed2262c7e0c7b66919f30505ef)) 47 | - select multiple, typings fixes, refactor ([463aa85](https://github.com/json-schema-form-element/jsfe/commit/463aa85d7ba22480513bc485ab4ad849e39c5402)) 48 | -------------------------------------------------------------------------------- /packages/material/README.md: -------------------------------------------------------------------------------- 1 | # JSON Schema Form Element — ***Material*** edition 2 | 3 | ```sh 4 | npm install @jsfe/material 5 | ``` 6 | 7 | - Consult the [documentation](../../README.md). 8 | - Open the [playground](https://jsfe.js.org). 9 | - Try the [examples](https://github.com/json-schema-form-element/examples#readme). 10 | 11 | --- 12 | 13 | # `packages/material/src/form.def.ts`: 14 | 15 | ## Exports 16 | 17 | | Kind | Name | Declaration | Module | Package | 18 | | --------------------------- | -------------- | ----------- | ------------------------------ | ------- | 19 | | `custom-element-definition` | `jsf-material` | JsfMaterial | /packages/material/src/form.js | | 20 | 21 | # `packages/material/src/form.ts`: 22 | 23 | ## class: `JsfMaterial`, `jsf-material` 24 | 25 | ### Superclass 26 | 27 | | Name | Module | Package | 28 | | ----- | ------ | ---------- | 29 | | `Jsf` | | @jsfe/form | 30 | 31 | ### Fields 32 | 33 | | Name | Privacy | Type | Default | Description | Inherited From | 34 | | ------------- | ------- | ------- | ---------- | ----------- | -------------- | 35 | | `widgets` | public | | `widgets` | | | 36 | | `styleSheets` | public | `array` | `[styles]` | | | 37 | 38 |
39 | 40 | ## Exports 41 | 42 | | Kind | Name | Declaration | Module | Package | 43 | | ---- | ------------- | ----------- | ----------------------------- | ------- | 44 | | `js` | `JsfMaterial` | JsfMaterial | packages/material/src/form.ts | | 45 | 46 | # `packages/material/src/index.ts`: 47 | 48 | ## Exports 49 | 50 | | Kind | Name | Declaration | Module | Package | 51 | | ---- | ------------- | ----------- | --------- | ------------------ | 52 | | `js` | `JsfMaterial` | JsfMaterial | ./form.js | | 53 | | `js` | `*` | \* | | ./widgets/index.js | 54 | | `js` | `*` | \* | | @jsfe/types | 55 | | `js` | `Jsf` | Jsf | | @jsfe/form | 56 | 57 | # `packages/material/src/styles.ts`: 58 | 59 | ## Variables 60 | 61 | | Name | Description | Type | 62 | | -------- | ----------- | ---- | 63 | | `styles` | | | 64 | 65 |
66 | 67 | ## Exports 68 | 69 | | Kind | Name | Declaration | Module | Package | 70 | | ---- | -------- | ----------- | ------------------------------- | ------- | 71 | | `js` | `styles` | styles | packages/material/src/styles.ts | | 72 | 73 | -------------------------------------------------------------------------------- /packages/material/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/material", 3 | "version": "0.3.0", 4 | "description": "Google Material w3 forms, auto-generated by JSON schemas.", 5 | "keywords": [ 6 | "json-schema", 7 | "google", 8 | "material", 9 | "forms", 10 | "generation", 11 | "declarative", 12 | "openapi", 13 | "mongodb", 14 | "page-builder" 15 | ], 16 | "homepage": "https://jsfe.js.org", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/json-schema-form-element/jsfe", 20 | "directory": "packages/material" 21 | }, 22 | "license": "ISC", 23 | "author": { 24 | "name": "Julian Cataldo", 25 | "email": "contact@juliancataldo.com", 26 | "url": "https://www.juliancataldo.com" 27 | }, 28 | "type": "module", 29 | "types": "./dist/esm/index.d.ts", 30 | "exports": { 31 | ".": "./dist/esm/index.js", 32 | "./scss": "./src/styles.scss", 33 | "./scss/*": "./src/widgets/*.scss", 34 | "./css": "./dist/esm/styles.css", 35 | "./jss": "./dist/esm/styles.js", 36 | "./min": "./dist/esm-min" 37 | }, 38 | "files": [ 39 | "./dist/esm", 40 | "./dist/esm-min", 41 | "./src/**/*.scss", 42 | "./vscode.html-custom-data.json", 43 | "./vscode.css-custom-data.json", 44 | "./custom-elements.json" 45 | ], 46 | "scripts": { 47 | "build": "pnpm clean ; pnpm ts:build ; pnpm css:build ; pnpm css:to-js", 48 | "clean": "rm -rf ./dist", 49 | "css:build": "pnpm sass --no-source-map src/styles.scss:dist/esm/styles.css", 50 | "css:dev": "pnpm sass --watch src/styles.scss:dist/esm/styles.css & pnpm css:to-js:dev", 51 | "css:to-js": "node ../../scripts/css-to-js.js dist/esm/styles.css", 52 | "css:to-js:dev": "nodemon dist/esm/styles.css -x 'pnpm css:to-js'", 53 | "dev": "pnpm ts:dev & pnpm css:dev & (sleep 3 && pnpm css:to-js:dev)", 54 | "ts:build": "pnpm tsc", 55 | "ts:dev": "pnpm tsc --watch" 56 | }, 57 | "dependencies": { 58 | "@jsfe/form": "workspace:*", 59 | "@jsfe/types": "workspace:*" 60 | }, 61 | "devDependencies": { 62 | "sass": "^1.69.5", 63 | "typescript": "^5.3.2" 64 | }, 65 | "peerDependencies": { 66 | "@material/material-color-utilities": "^0.2.7", 67 | "@material/web": "^1.0.0", 68 | "lit": "^3.1.0" 69 | }, 70 | "publishConfig": { 71 | "access": "public" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/material/src/form.def.ts: -------------------------------------------------------------------------------- 1 | import { JsfMaterial } from './form.js'; 2 | 3 | customElements.define('jsf-material', JsfMaterial); 4 | 5 | declare global { 6 | interface HTMLElementTagNameMap { 7 | 'jsf-material': JsfMaterial; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/material/src/form.ts: -------------------------------------------------------------------------------- 1 | import { Jsf } from '@jsfe/form'; 2 | import * as widgets from './widgets/index.js'; 3 | import { styles } from './styles.js'; 4 | 5 | export class JsfMaterial extends Jsf { 6 | public widgets = widgets; 7 | 8 | public styleSheets = [styles]; 9 | } 10 | -------------------------------------------------------------------------------- /packages/material/src/index.ts: -------------------------------------------------------------------------------- 1 | import './form.def.js'; 2 | 3 | export { JsfMaterial } from './form.js'; 4 | export * as widgets from './widgets/index.js'; 5 | 6 | export type * from '@jsfe/types'; 7 | export type { Jsf } from '@jsfe/form'; 8 | -------------------------------------------------------------------------------- /packages/material/src/styles.scss: -------------------------------------------------------------------------------- 1 | // keep-sorted start 2 | @import './widgets/_all.scss'; 3 | @import './widgets/_fieldset.scss'; 4 | @import './widgets/_toggle.scss'; 5 | @import './widgets/array.scss'; 6 | @import './widgets/callout.scss'; 7 | @import './widgets/object.scss'; 8 | @import './widgets/range.scss'; 9 | @import './widgets/submit.scss'; 10 | @import './widgets/switch.scss'; 11 | // keep-sorted end 12 | -------------------------------------------------------------------------------- /packages/material/src/styles.ts: -------------------------------------------------------------------------------- 1 | /* STUB */ 2 | 3 | import { css } from 'lit'; 4 | 5 | export const styles = css` 6 | /* STUB - Compiled SCSS goes here */ 7 | `; 8 | -------------------------------------------------------------------------------- /packages/material/src/widgets/_all.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | --md-ref-typeface-brand: 'Roboto'; 3 | --md-ref-typeface-plain: system-ui; 4 | } 5 | 6 | .theme-material { 7 | font-family: Roboto, system-ui; 8 | } 9 | 10 | // .material { 11 | // &[part='field-checkbox'] { 12 | // /* cursor: pointer; */ 13 | // /* width: 100%; */ 14 | // display: flex; 15 | // /* gap: 1em; */ 16 | // align-items: center; 17 | 18 | // small { 19 | // color: var(--md-sys-color-on-surface-variant); 20 | // } 21 | // } 22 | 23 | // // Enumerations 24 | // &[part='field-select'] { 25 | // --md-menu-container-color: var(--md-sys-color-background); 26 | 27 | // &, 28 | // md-select-option, 29 | // md-menu { 30 | // width: 100%; 31 | // } 32 | 33 | // &::part(menu) { 34 | // width: 100%; 35 | // } 36 | // } 37 | // } 38 | 39 | // .dummy { 40 | // --md-sys-color-primary: #88ceff; 41 | // --md-sys-color-on-primary: #00344d; 42 | // --md-sys-color-primary-container: #004c6d; 43 | // --md-sys-color-on-primary-container: #c8e6ff; 44 | // --md-sys-color-secondary: #b7c9d8; 45 | // --md-sys-color-on-secondary: #21323f; 46 | // --md-sys-color-secondary-container: #384956; 47 | // --md-sys-color-on-secondary-container: #d3e5f5; 48 | // --md-sys-color-tertiary: #cec0e8; 49 | // --md-sys-color-on-tertiary: #352b4b; 50 | // --md-sys-color-tertiary-container: #4b4163; 51 | // --md-sys-color-on-tertiary-container: #e9ddff; 52 | // --md-sys-color-error: #ffb4ab; 53 | // --md-sys-color-on-error: #690005; 54 | // --md-sys-color-error-container: #93000a; 55 | // --md-sys-color-on-error-container: #ffb4ab; 56 | // --md-sys-color-background: #191c1e; 57 | // --md-sys-color-on-background: #e2e2e5; 58 | // --md-sys-color-surface: #191c1e; 59 | // --md-sys-color-on-surface: #e2e2e5; 60 | // --md-sys-color-surface-variant: #41484d; 61 | // --md-sys-color-on-surface-variant: #c1c7ce; 62 | // --md-sys-color-outline: #8b9198; 63 | // --md-sys-color-outline-variant: #41484d; 64 | // --md-sys-color-shadow: #000; 65 | // --md-sys-color-scrim: #000; 66 | // --md-sys-color-inverse-surface: #e2e2e5; 67 | // --md-sys-color-inverse-on-surface: #2e3133; 68 | // --md-sys-color-inverse-primary: #006590; 69 | // } 70 | -------------------------------------------------------------------------------- /packages/material/src/widgets/_fieldset.scss: -------------------------------------------------------------------------------- 1 | .theme-material.widget-fieldset { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | gap: 1.5em 0; 6 | padding: 2em 1em; 7 | margin: 0; 8 | font-weight: 300; 9 | border: none; 10 | // border: 1px solid transparent; 11 | border-radius: 1rem; 12 | transition-timing-function: ease-in-out; 13 | transition-duration: 250ms; 14 | 15 | legend { 16 | width: 100%; 17 | font-size: 1.5em; 18 | border-bottom: 1px solid var(--md-sys-color-inverse-on-surface); 19 | } 20 | 21 | .widget-object__description { 22 | margin: 0 0 1rem 0; 23 | opacity: calc(3 / 4); 24 | } 25 | 26 | --md-sys-color-shadow: var(--md-sys-color-inverse-on-surface); 27 | --md-elevation-level: 0; 28 | 29 | &:hover { 30 | --md-elevation-level: 4; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/material/src/widgets/_toggle.scss: -------------------------------------------------------------------------------- 1 | .theme-material.widget-toggle { 2 | display: flex; 3 | gap: 1em; 4 | align-items: center; 5 | justify-content: space-between; 6 | cursor: pointer; 7 | padding: 0.5rem; 8 | 9 | small { 10 | color: var(--md-sys-color-on-surface-variant); 11 | } 12 | 13 | user-select: none; 14 | 15 | & + & { 16 | border-top: 1px solid var(--md-sys-color-inverse-on-surface); 17 | margin-top: -0.5rem; 18 | padding-top: 1.5rem; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/material/src/widgets/array.scss: -------------------------------------------------------------------------------- 1 | .theme-material.widget-array { 2 | --md-sys-color-shadow: var(--md-sys-color-inverse-on-surface); 3 | --md-elevation-level: 0; 4 | 5 | &:hover { 6 | --md-elevation-level: 4; 7 | } 8 | 9 | .widget-array__card { 10 | width: 100%; 11 | // transition: outline var(--sl-transition-slow); 12 | 13 | transition: box-shadow var(--sl-transition-medium); 14 | 15 | &:hover { 16 | // background: var(--sl-color-gray-50); 17 | box-shadow: var(--sl-shadow-medium); 18 | } 19 | 20 | &[data-dropzone] { 21 | border-radius: var(--sl-border-radius-medium); 22 | outline: 1px solid var(--sl-color-primary-500); 23 | // transition: outline var(--sl-transition-fast); 24 | 25 | // cursor: grab; 26 | 27 | * { 28 | pointer-events: none; 29 | } 30 | } 31 | } 32 | 33 | sl-card::part(body) { 34 | padding: var(--sl-spacing-medium) var(--sl-spacing-small); 35 | } 36 | 37 | .widget-array__header { 38 | display: flex; 39 | align-items: center; 40 | justify-content: space-between; 41 | width: 100%; 42 | font-size: 0.8em; 43 | user-select: none; 44 | //// sl-icon::pa { 45 | //// height: 0.2rem; 46 | //// } 47 | //// display: none; 48 | 49 | sl-tag::part(base) { 50 | background: var(--sl-color-neutral-100); 51 | } 52 | } 53 | 54 | .widget-array__handle { 55 | display: flex; 56 | flex-grow: 1; 57 | align-items: center; 58 | justify-content: space-between; 59 | height: 2rem; 60 | padding-left: var(--sl-spacing-2x-small); 61 | margin: 0 var(--sl-spacing-medium) 0 0; 62 | font-size: 1.25em; 63 | color: var(--sl-color-neutral-500); 64 | // cursor: grab; 65 | cursor: move; 66 | transition: opacity, var(--sl-transition-fast); 67 | 68 | &:hover { 69 | color: var(--sl-color-neutral-600); 70 | background: var(--sl-color-neutral-100); 71 | border-radius: var(--sl-border-radius-x-large); 72 | transition: var(--sl-transition-medium); 73 | } 74 | 75 | &:active { 76 | // cursor: grabbing; 77 | user-select: none; 78 | } 79 | 80 | .widget-array__handle-grip { 81 | display: flex; 82 | flex-grow: 1; 83 | justify-content: center; 84 | } 85 | } 86 | 87 | .widget-array__add-zone { 88 | display: flex; 89 | padding: var(--sl-spacing-2x-large) var(--sl-spacing-2x-large); 90 | border: 2px dashed var(--sl-color-gray-100); 91 | border-radius: var(--sl-border-radius-large); 92 | box-shadow: var(--sl-shadow-large) inset; 93 | 94 | & > sl-button { 95 | width: 100%; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /packages/material/src/widgets/array.ts: -------------------------------------------------------------------------------- 1 | import type { Widgets } from '@jsfe/types'; 2 | import { nothing, html } from 'lit'; 3 | 4 | import '@material/web/button/outlined-button'; 5 | 6 | export const array: Widgets['array'] = (options) => { 7 | return html` 8 |
13 | ${options.label ? html`${options.label}` : nothing} 14 | 15 | ${options.helpText 16 | ? html`

${options.helpText}

` 17 | : nothing} 18 | 19 | ${options.items( 20 | (index, widget, controls) => html` 21 |
28 | 29 |
30 |
36 | ${index + 1} 37 |
38 | 39 |
40 |
41 | 42 | 76 |
77 | 78 | ${widget} 79 |
80 | `, 81 | )} 82 | 83 | 91 |
92 | `; 93 | }; 94 | -------------------------------------------------------------------------------- /packages/material/src/widgets/callout.scss: -------------------------------------------------------------------------------- 1 | // NOTE: Extracted from Shoelace docs website 2 | 3 | /* Callouts */ 4 | .theme-material[part='widget-callout'] { 5 | position: relative; 6 | padding: 1rem 1.5rem 1rem 2rem; 7 | margin: 1rem 0; 8 | 9 | color: var(--md-sys-color-error); 10 | // background-color: var(--md-sys-color-error-container); 11 | border: solid 1px var(--md-sys-color-error); 12 | border-color: var(--md-sys-color-error-container); 13 | border-left: solid 3px var(--md-sys-color-error); 14 | border-radius: 0.25rem; 15 | 16 | & > :first-child { 17 | margin-top: 0; 18 | } 19 | 20 | & > :last-child { 21 | margin-bottom: 0; 22 | } 23 | 24 | // &.callout--tip { 25 | // color: var(--sl-color-primary-800); 26 | // background-color: var(--sl-color-primary-100); 27 | // border-left-color: var(--sl-color-primary-600); 28 | // } 29 | 30 | &::before { 31 | position: absolute; 32 | top: calc(50% - 0.8rem); 33 | left: calc(-0.8rem - 2px); 34 | display: flex; 35 | align-items: center; 36 | justify-content: center; 37 | width: 1.6rem; 38 | height: 1.6rem; 39 | clip-path: circle(50% at 50% 50%); 40 | // font-family: var(--sl-font-serif); 41 | // font-weight: var(--sl-font-weight-bold); 42 | color: var(--md-sys-color-on-error); 43 | content: ''; 44 | } 45 | 46 | // &.callout--tip::before { 47 | // font-style: italic; 48 | // content: 'i'; 49 | // background-color: var(--sl-color-primary-600); 50 | // } 51 | 52 | &.callout--warning { 53 | // color: var(--sl-color-warning-800); 54 | // background-color: var(--sl-color-warning-100); 55 | // border-left-color: var(--sl-color-warning-600); 56 | } 57 | 58 | &.callout--warning::before { 59 | content: '!'; 60 | background-color: var(--md-sys-color-error); 61 | } 62 | 63 | // &.callout--danger { 64 | // color: var(--sl-color-danger-800); 65 | // background-color: var(--sl-color-danger-100); 66 | // border-left-color: var(--sl-color-danger-600); 67 | // } 68 | 69 | // &.callout--danger::before { 70 | // content: '‼'; 71 | // background-color: var(--sl-color-danger-600); 72 | // } 73 | 74 | // & + & { 75 | // margin-top: calc(-0.5 * var(--docs-content-vertical-spacing)); 76 | // } 77 | 78 | // a { 79 | // color: inherit; 80 | // } 81 | } 82 | -------------------------------------------------------------------------------- /packages/material/src/widgets/callout.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { classMap } from 'lit/directives/class-map.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | export const callout: Widgets['callout'] = (options) => { 7 | console.warn(options.message); 8 | return html` 9 | 17 | `; 18 | }; 19 | 20 | export const flag = (feature: string) => { 21 | const message = `\`${feature}\` is not supported yet, but you can preview it with \`experimental = { ${feature}: true, ... }\``; 22 | console.error(message); 23 | return html` 24 | 33 | `; 34 | }; 35 | -------------------------------------------------------------------------------- /packages/material/src/widgets/checkbox.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@material/web/checkbox/checkbox.js'; 6 | import type { MdCheckbox } from '@material/web/checkbox/checkbox.js'; 7 | 8 | export const checkbox: Widgets['checkbox'] = (options) => html` 9 | 34 | `; 35 | -------------------------------------------------------------------------------- /packages/material/src/widgets/date.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@material/web/textfield/outlined-text-field.js'; 6 | import type { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field.js'; 7 | 8 | export const date: Widgets['date'] = (options) => html` 9 | { 19 | const { valueAsDate: newValue } = event.target as MdOutlinedTextField; 20 | 21 | if (newValue) options.valueChangedCallback?.(newValue); 22 | }} 23 | @keydown=${options.handleKeydown} 24 | > 25 | 26 | 31 | `; 32 | -------------------------------------------------------------------------------- /packages/material/src/widgets/index.ts: -------------------------------------------------------------------------------- 1 | // import './defs/range.def.js'; 2 | 3 | // keep-sorted start 4 | export { callout } from './callout.js'; 5 | export { checkbox } from './checkbox.js'; 6 | export { date } from './date.js'; 7 | export { number } from './number.js'; 8 | export { object } from './object.js'; 9 | export { range } from './range.js'; 10 | export { select } from './select.js'; 11 | export { submit } from './submit.js'; 12 | // export { array } from './array.js'; 13 | export { switchh as switch } from './switch.js'; 14 | export { text } from './text.js'; 15 | export { textarea } from './textarea.js'; 16 | // keep-sorted end 17 | -------------------------------------------------------------------------------- /packages/material/src/widgets/number.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@material/web/textfield/outlined-text-field.js'; 7 | import type { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field.js'; 8 | 9 | export const number: Widgets['number'] = (options) => html` 10 | { 23 | const { valueAsNumber: newValue } = event.target as MdOutlinedTextField; 24 | options.valueChangedCallback?.(newValue); 25 | }} 26 | @keydown=${options.handleKeydown} 27 | > 28 | 29 | `; 30 | -------------------------------------------------------------------------------- /packages/material/src/widgets/object.scss: -------------------------------------------------------------------------------- 1 | .theme-material.widget-object { 2 | } 3 | -------------------------------------------------------------------------------- /packages/material/src/widgets/object.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable arrow-body-style */ 2 | import type { Widgets } from '@jsfe/types'; 3 | import { nothing, html } from 'lit'; 4 | import '@material/web/elevation/elevation.js'; 5 | 6 | export const object: Widgets['object'] = (options) => { 7 | return html` 8 |
13 | 14 | 15 | ${options.label ? html`${options.label}` : nothing} 16 | 17 | ${options.helpText 18 | ? html`

${options.helpText}

` 19 | : nothing} 20 | 21 | ${options.children} 22 |
23 | `; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/material/src/widgets/range.scss: -------------------------------------------------------------------------------- 1 | .theme-material.widget-range { 2 | display: flex; 3 | display: inline-flex; 4 | flex-direction: column; 5 | gap: 0; 6 | /* place-items: center; */ 7 | gap: 8px; 8 | place-items: flex-start; 9 | width: 100%; 10 | font-family: Roboto, system-ui; 11 | color: var(--md-sys-color-on-background, #000); 12 | } 13 | 14 | md-slider { 15 | width: 100%; 16 | /* --md-slider-focus-handle-color: red; */ 17 | --md-slider-active-track-color: var(--md-sys-color-primary); 18 | --md-slider-inactive-track-color: var(--md-sys-on-surface); 19 | } 20 | -------------------------------------------------------------------------------- /packages/material/src/widgets/range.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@material/web/slider/slider.js'; 7 | import type { MdSlider } from '@material/web/slider/slider.js'; 8 | 9 | export const range: Widgets['range'] = (options) => html` 10 | 28 | `; 29 | -------------------------------------------------------------------------------- /packages/material/src/widgets/select.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@material/web/select/outlined-select.js'; 6 | import '@material/web/select/select-option.js'; 7 | import type { MdOutlinedSelect } from '@material/web/select/outlined-select.js'; 8 | 9 | export const select: Widgets['select'] = (options) => html` 10 | { 15 | // value=${ifDefined(options.value)} 16 | // .supportingText=${options.helpText} 17 | let newValue: string | null | number | string[] = ( 18 | event.target as MdOutlinedSelect 19 | ).value; 20 | if (Array.isArray(newValue)) return; 21 | if (options.type === 'number' || options.type === 'integer') { 22 | newValue = Number(newValue); 23 | } 24 | // (event.target as MdOutlinedSelect).selectIndex(0); 25 | options.valueChangedCallback?.(newValue); 26 | }} 27 | class="material" 28 | >${options.enum?.map( 29 | (enumValue, i) => 30 | html` 31 | ${enumValue} 32 | `, 33 | )} 35 | `; 36 | -------------------------------------------------------------------------------- /packages/material/src/widgets/submit.scss: -------------------------------------------------------------------------------- 1 | .theme-material.widget-submit { 2 | display: flex; 3 | justify-content: center; 4 | margin: var(--sl-spacing-2x-large) 0; 5 | font-size: 4em; 6 | } 7 | -------------------------------------------------------------------------------- /packages/material/src/widgets/submit.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@material/web/button/filled-button.js'; 6 | 7 | export const submit: Widgets['submit'] = (options) => html` 8 | 9 |
10 | ${options.label ?? 'Submit'} 13 |
14 | `; 15 | -------------------------------------------------------------------------------- /packages/material/src/widgets/switch.scss: -------------------------------------------------------------------------------- 1 | .theme-material.widget-switch { 2 | // FIXME: 3 | // md-switch { 4 | // cursor: pointer; 5 | // } 6 | } 7 | -------------------------------------------------------------------------------- /packages/material/src/widgets/switch.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@material/web/switch/switch.js'; 6 | import type { MdSwitch } from '@material/web/switch/switch.js'; 7 | 8 | export const switchh: Widgets['switch'] = (options) => html` 9 | 34 | `; 35 | -------------------------------------------------------------------------------- /packages/material/src/widgets/text.scss: -------------------------------------------------------------------------------- 1 | md-outlined-text-field { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /packages/material/src/widgets/text.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@material/web/textfield/outlined-text-field.js'; 7 | import type { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field.js'; 8 | 9 | export const text: Widgets['text'] = (options) => html` 10 | { 23 | const { value: newValue } = event.target as MdOutlinedTextField; 24 | options.valueChangedCallback?.(newValue); 25 | }} 26 | @keydown=${options.handleKeydown} 27 | > 28 | 29 | `; 30 | -------------------------------------------------------------------------------- /packages/material/src/widgets/textarea.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@material/web/textfield/outlined-text-field.js'; 7 | import type { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field.js'; 8 | 9 | export const textarea: Widgets['textarea'] = (options) => html` 10 | { 23 | const { value: newValue } = event.target as MdOutlinedTextField; 24 | options.valueChangedCallback?.(newValue); 25 | }} 26 | rows=${4} 27 | > 28 | 29 | `; 30 | -------------------------------------------------------------------------------- /packages/material/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "Bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "outDir": "dist/esm", 22 | "declaration": true, 23 | 24 | "removeComments": true, 25 | 26 | "noImplicitAny": true, 27 | 28 | "declarationMap": true 29 | }, 30 | "include": [ 31 | // 32 | "src/index.ts" 33 | ], 34 | "exclude": ["src/styles.ts"] 35 | } 36 | -------------------------------------------------------------------------------- /packages/shoelace/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.4.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/shoelace@0.3.1...@jsfe/shoelace@0.4.0) (2024-06-06) 7 | 8 | ### Bug Fixes 9 | 10 | - add types field to package.json ([#8](https://github.com/json-schema-form-element/jsfe/issues/8)) ([e0ecf92](https://github.com/json-schema-form-element/jsfe/commit/e0ecf923475ca123ea9ee76d331d35d68c591102)) 11 | 12 | ### Features 13 | 14 | - submit button custom text ([e751828](https://github.com/json-schema-form-element/jsfe/commit/e751828b3bc07d21fc537b97e1b531a94a77fd70)), closes [#7](https://github.com/json-schema-form-element/jsfe/issues/7) 15 | 16 | ### Reverts 17 | 18 | - "style: bulk reformat with new imports orders" ([1236078](https://github.com/json-schema-form-element/jsfe/commit/12360786626927f2c8a0273b245b906e5946b920)) 19 | 20 | ## [0.3.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/shoelace@0.3.0...@jsfe/shoelace@0.3.1) (2023-11-30) 21 | 22 | **Note:** Version bump only for package @jsfe/shoelace 23 | 24 | # [0.3.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/shoelace@0.2.0...@jsfe/shoelace@0.3.0) (2023-11-30) 25 | 26 | ### Features 27 | 28 | - "disabled" and "read only" ui schema options ([01952e2](https://github.com/json-schema-form-element/jsfe/commit/01952e23355ffd5ce3cd13f8a8e5de4b05cc852c)) 29 | 30 | # [0.2.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/shoelace@0.1.1...@jsfe/shoelace@0.2.0) (2023-11-30) 31 | 32 | ### Bug Fixes 33 | 34 | - date-time widget coercition ([de1f6a8](https://github.com/json-schema-form-element/jsfe/commit/de1f6a88afef6efcabcdc4ebab1ca6d2897178a4)) 35 | 36 | ### Features 37 | 38 | - more system / wired widgets, clean others ([bfcac92](https://github.com/json-schema-form-element/jsfe/commit/bfcac9247ded39af312b2df99a21a6d94d37c965)) 39 | - ui schema title + desc., better date formats ([208e596](https://github.com/json-schema-form-element/jsfe/commit/208e59600d2a6811d0596572f1bc1ea4f240a945)) 40 | 41 | ## [0.1.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/shoelace@0.1.0...@jsfe/shoelace@0.1.1) (2023-10-12) 42 | 43 | ### Bug Fixes 44 | 45 | - better styles, material export ([9a33cbb](https://github.com/json-schema-form-element/jsfe/commit/9a33cbb29059ac8827647db6a7deda45d9cb3c09)) 46 | 47 | # 0.1.0 (2023-10-11) 48 | 49 | ### Features 50 | 51 | - add base packages files ([d8e42bd](https://github.com/json-schema-form-element/jsfe/commit/d8e42bdcda5f8af5e2728e1556946d333e7f59b5)) 52 | - add components for all ui libraries packages ([7a6d3a5](https://github.com/json-schema-form-element/jsfe/commit/7a6d3a53f3939d00512c9f42925d1f9f1db246ff)) 53 | - re-organize to mono-repo ([3888c62](https://github.com/json-schema-form-element/jsfe/commit/3888c62a07b07aed2262c7e0c7b66919f30505ef)) 54 | - select multiple, typings fixes, refactor ([463aa85](https://github.com/json-schema-form-element/jsfe/commit/463aa85d7ba22480513bc485ab4ad849e39c5402)) 55 | -------------------------------------------------------------------------------- /packages/shoelace/README.md: -------------------------------------------------------------------------------- 1 | # JSON Schema Form Element — ***Shoelace*** edition 2 | 3 | ```sh 4 | npm install @jsfe/shoelace 5 | ``` 6 | 7 | - Consult the [documentation](../../README.md). 8 | - Open the [playground](https://jsfe.js.org). 9 | - Try the [examples](https://github.com/json-schema-form-element/examples#readme). 10 | 11 | --- 12 | 13 | # `packages/shoelace/src/form.def.ts`: 14 | 15 | ## Exports 16 | 17 | | Kind | Name | Declaration | Module | Package | 18 | | --------------------------- | -------------- | ----------- | ------------------------------ | ------- | 19 | | `custom-element-definition` | `jsf-shoelace` | JsfShoelace | /packages/shoelace/src/form.js | | 20 | 21 | # `packages/shoelace/src/form.ts`: 22 | 23 | ## class: `JsfShoelace`, `jsf-shoelace` 24 | 25 | ### Superclass 26 | 27 | | Name | Module | Package | 28 | | ----- | ------ | ---------- | 29 | | `Jsf` | | @jsfe/form | 30 | 31 | ### Fields 32 | 33 | | Name | Privacy | Type | Default | Description | Inherited From | 34 | | ------------- | ------- | ------- | ---------- | ----------- | -------------- | 35 | | `widgets` | public | | `widgets` | | | 36 | | `styleSheets` | public | `array` | `[styles]` | | | 37 | 38 |
39 | 40 | ## Exports 41 | 42 | | Kind | Name | Declaration | Module | Package | 43 | | ---- | ------------- | ----------- | ----------------------------- | ------- | 44 | | `js` | `JsfShoelace` | JsfShoelace | packages/shoelace/src/form.ts | | 45 | 46 | # `packages/shoelace/src/index.ts`: 47 | 48 | ## Exports 49 | 50 | | Kind | Name | Declaration | Module | Package | 51 | | ---- | ------------- | ----------- | --------- | ------------------ | 52 | | `js` | `JsfShoelace` | JsfShoelace | ./form.js | | 53 | | `js` | `*` | \* | | ./widgets/index.js | 54 | | `js` | `*` | \* | | @jsfe/types | 55 | | `js` | `Jsf` | Jsf | | @jsfe/form | 56 | 57 | # `packages/shoelace/src/styles.ts`: 58 | 59 | ## Variables 60 | 61 | | Name | Description | Type | 62 | | -------- | ----------- | ---- | 63 | | `styles` | | | 64 | 65 |
66 | 67 | ## Exports 68 | 69 | | Kind | Name | Declaration | Module | Package | 70 | | ---- | -------- | ----------- | ------------------------------- | ------- | 71 | | `js` | `styles` | styles | packages/shoelace/src/styles.ts | | 72 | 73 | -------------------------------------------------------------------------------- /packages/shoelace/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/shoelace", 3 | "version": "0.4.0", 4 | "description": "Shoelace v2 forms, auto-generated by JSON schemas.", 5 | "keywords": [ 6 | "json-schema", 7 | "shoelace", 8 | "forms", 9 | "generation", 10 | "declarative", 11 | "openapi", 12 | "mongodb", 13 | "page-builder" 14 | ], 15 | "homepage": "https://jsfe.js.org", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/json-schema-form-element/jsfe", 19 | "directory": "packages/shoelace" 20 | }, 21 | "license": "ISC", 22 | "author": { 23 | "name": "Julian Cataldo", 24 | "email": "contact@juliancataldo.com", 25 | "url": "https://www.juliancataldo.com" 26 | }, 27 | "type": "module", 28 | "types": "./dist/esm/index.d.ts", 29 | "exports": { 30 | ".": "./dist/esm/index.js", 31 | "./scss": "./src/styles.scss", 32 | "./scss/*": "./src/widgets/*.scss", 33 | "./css": "./dist/esm/styles.css", 34 | "./jss": "./dist/esm/styles.js", 35 | "./min": "./dist/esm-min" 36 | }, 37 | "files": [ 38 | "./dist/esm", 39 | "./dist/esm-min", 40 | "./src/**/*.scss", 41 | "./vscode.html-custom-data.json", 42 | "./vscode.css-custom-data.json", 43 | "./custom-elements.json" 44 | ], 45 | "scripts": { 46 | "build": "pnpm clean ; pnpm ts:build ; pnpm css:build ; pnpm css:to-js", 47 | "clean": "rm -rf ./dist", 48 | "css:build": "pnpm sass --no-source-map src/styles.scss:dist/esm/styles.css", 49 | "css:dev": "pnpm sass --watch src/styles.scss:dist/esm/styles.css & pnpm css:to-js:dev", 50 | "css:to-js": "node ../../scripts/css-to-js.js dist/esm/styles.css", 51 | "css:to-js:dev": "nodemon dist/esm/styles.css -x 'pnpm css:to-js'", 52 | "dev": "pnpm ts:dev & pnpm css:dev & (sleep 3 && pnpm css:to-js:dev)", 53 | "ts:build": "pnpm tsc", 54 | "ts:dev": "pnpm tsc --watch" 55 | }, 56 | "dependencies": { 57 | "@jsfe/form": "workspace:*", 58 | "@jsfe/types": "workspace:*" 59 | }, 60 | "devDependencies": { 61 | "sass": "^1.69.5", 62 | "typescript": "^5.3.2" 63 | }, 64 | "peerDependencies": { 65 | "@shoelace-style/shoelace": "^2.12.0", 66 | "lit": "^3.1.0" 67 | }, 68 | "publishConfig": { 69 | "access": "public" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/shoelace/src/form.def.ts: -------------------------------------------------------------------------------- 1 | import { JsfShoelace } from './form.js'; 2 | 3 | customElements.define('jsf-shoelace', JsfShoelace); 4 | 5 | declare global { 6 | interface HTMLElementTagNameMap { 7 | 'jsf-shoelace': JsfShoelace; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/shoelace/src/form.ts: -------------------------------------------------------------------------------- 1 | import { Jsf } from '@jsfe/form'; 2 | import * as widgets from './widgets/index.js'; 3 | import { styles } from './styles.js'; 4 | 5 | export class JsfShoelace extends Jsf { 6 | public widgets = widgets; 7 | 8 | public styleSheets = [styles]; 9 | } 10 | -------------------------------------------------------------------------------- /packages/shoelace/src/index.ts: -------------------------------------------------------------------------------- 1 | import './form.def.js'; 2 | 3 | export { JsfShoelace } from './form.js'; 4 | export * as widgets from './widgets/index.js'; 5 | 6 | export type * from '@jsfe/types'; 7 | export type { Jsf } from '@jsfe/form'; 8 | -------------------------------------------------------------------------------- /packages/shoelace/src/styles.scss: -------------------------------------------------------------------------------- 1 | // keep-sorted start 2 | @import './widgets/_all.scss'; 3 | @import './widgets/_fieldset.scss'; 4 | @import './widgets/array.scss'; 5 | @import './widgets/callout.scss'; 6 | @import './widgets/checkbox-group.scss'; 7 | @import './widgets/checkbox.scss'; 8 | @import './widgets/color-picker.scss'; 9 | @import './widgets/object.scss'; 10 | @import './widgets/radio-group-boolean.scss'; 11 | @import './widgets/radio-group.scss'; 12 | @import './widgets/rating.scss'; 13 | @import './widgets/submit.scss'; 14 | // keep-sorted end 15 | -------------------------------------------------------------------------------- /packages/shoelace/src/styles.ts: -------------------------------------------------------------------------------- 1 | /* STUB */ 2 | 3 | import { css } from 'lit'; 4 | 5 | export const styles = css` 6 | /* STUB - Compiled SCSS goes here */ 7 | `; 8 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/_all.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | box-sizing: border-box; 3 | font: 16px var(--sl-font-sans); 4 | font-weight: var(--sl-font-weight-normal); 5 | line-height: var(--sl-line-height-normal); 6 | color: var(--sl-color-neutral-900); 7 | -moz-osx-font-smoothing: grayscale; 8 | -webkit-font-smoothing: antialiased; 9 | 10 | *, 11 | *::before, 12 | *::after { 13 | box-sizing: inherit; 14 | } 15 | 16 | // NOTE: is it bad? 17 | --sl-input-focus-ring-color: var(--sl-color-primary-50); 18 | } 19 | 20 | // ----------------------------------------------------------------------------- 21 | 22 | // WIP: For system, might be ported for MWC and shoelace 23 | ::selection { 24 | color: var(--sl-color-primary-300); 25 | background-color: var(--sl-color-primary-950); 26 | } 27 | 28 | // ----------------------------------------------------------------------------- 29 | 30 | // NOTE: From shoelace docs examples 31 | 32 | sl-input, 33 | sl-select, 34 | sl-checkbox { 35 | display: block; 36 | // margin-bottom: var(--sl-spacing-medium); 37 | } 38 | 39 | /* User invalid styles */ 40 | sl-input[data-user-invalid]::part(base), 41 | sl-select[data-user-invalid]::part(combobox), 42 | sl-checkbox[data-user-invalid]::part(control) { 43 | border-color: var(--sl-color-danger-600); 44 | } 45 | 46 | [data-user-invalid]::part(form-control-label), 47 | [data-user-invalid]::part(form-control-help-text), 48 | sl-checkbox[data-user-invalid]::part(label) { 49 | color: var(--sl-color-danger-700); 50 | } 51 | 52 | sl-checkbox[data-user-invalid]::part(control) { 53 | outline: none; 54 | } 55 | 56 | sl-input:focus-within[data-user-invalid]::part(base), 57 | sl-select:focus-within[data-user-invalid]::part(combobox), 58 | sl-checkbox:focus-within[data-user-invalid]::part(control) { 59 | border-color: var(--sl-color-danger-600); 60 | box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-danger-300); 61 | } 62 | 63 | // NOTE: Unused for now 64 | // /* User valid styles */ 65 | // sl-input[data-user-valid]::part(base), 66 | // sl-select[data-user-valid]::part(combobox), 67 | // sl-checkbox[data-user-valid]::part(control) { 68 | // border-color: var(--sl-color-success-600); 69 | // } 70 | 71 | // [data-user-valid]::part(form-control-label), 72 | // [data-user-valid]::part(form-control-help-text), 73 | // sl-checkbox[data-user-valid]::part(label) { 74 | // color: var(--sl-color-success-700); 75 | // } 76 | 77 | // sl-checkbox[data-user-valid]::part(control) { 78 | // background-color: var(--sl-color-success-600); 79 | // outline: none; 80 | // } 81 | 82 | // sl-input:focus-within[data-user-valid]::part(base), 83 | // sl-select:focus-within[data-user-valid]::part(combobox), 84 | // sl-checkbox:focus-within[data-user-valid]::part(control) { 85 | // border-color: var(--sl-color-success-600); 86 | // box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-color-success-300); 87 | // } 88 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/_fieldset.scss: -------------------------------------------------------------------------------- 1 | .theme-shoelace.widget-fieldset { 2 | display: flex; 3 | flex-direction: column; 4 | gap: var(--sl-spacing-x-large) 0; 5 | padding: var(--sl-spacing-medium) var(--sl-spacing-small); 6 | margin: 0; 7 | font-weight: var(--sl-font-weight-semibold); 8 | border: 1px solid var(--sl-color-neutral-50); 9 | border-radius: var(--sl-border-radius-large); 10 | 11 | .widget-fieldset__description { 12 | font-size: var(--sl-input-help-text-font-size-medium); 13 | color: var(--sl-input-help-text-color); 14 | } 15 | 16 | & & { 17 | transition: box-shadow var(--sl-transition-medium); 18 | 19 | &:hover { 20 | // background: var(--sl-color-gray-50); 21 | box-shadow: var(--sl-shadow-medium); 22 | } 23 | } 24 | 25 | legend { 26 | // font-size: var(--sl-font-size-large); 27 | } 28 | 29 | &.level-0 { 30 | legend { 31 | font-size: var(--sl-font-size-2x-large); 32 | font-weight: 200; 33 | } 34 | 35 | sl-card::part(body) { 36 | // background-color: slcolo; 37 | } 38 | // --sl-panel-background-color: var(--sl-color-neutral-50); 39 | } 40 | 41 | &.level-1 { 42 | legend { 43 | font-size: var(--sl-font-size-x-large); 44 | font-weight: var(--sl-font-weight-light); 45 | } 46 | 47 | sl-card::part(header) { 48 | // background-color: var(--sl-color-neutral-50); 49 | } 50 | // --sl-panel-background-color: var(--sl-color-neutral-50); 51 | } 52 | 53 | &.level-2 { 54 | legend { 55 | font-size: var(--sl-font-size-large); 56 | font-weight: var(--sl-font-weight-light); 57 | } 58 | 59 | sl-card::part(header) { 60 | // background-color: var(--sl-color-neutral-100); 61 | } 62 | 63 | // --sl-panel-background-color: var(--sl-color-neutral-100); 64 | } 65 | 66 | &.level-3 { 67 | legend { 68 | font-size: var(--sl-font-size-medium); 69 | font-weight: var(--sl-font-weight-bold); 70 | } 71 | 72 | sl-card::part(header) { 73 | // background-color: var(--sl-color-neutral-200); 74 | } 75 | // --sl-panel-background-color: var(--sl-color-neutral-200); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/array.scss: -------------------------------------------------------------------------------- 1 | .theme-shoelace.widget-array { 2 | .widget-array__card { 3 | // width: 100%; 4 | // transition: outline var(--sl-transition-slow); 5 | 6 | transition: box-shadow var(--sl-transition-medium); 7 | 8 | &:hover { 9 | // background: var(--sl-color-gray-50); 10 | box-shadow: var(--sl-shadow-medium); 11 | } 12 | 13 | &[data-dropzone] { 14 | border-radius: var(--sl-border-radius-medium); 15 | outline: 1px solid var(--sl-color-primary-500); 16 | // transition: outline var(--sl-transition-fast); 17 | 18 | // cursor: grab; 19 | 20 | * { 21 | pointer-events: none; 22 | } 23 | } 24 | } 25 | 26 | sl-card::part(body) { 27 | padding: var(--sl-spacing-medium) var(--sl-spacing-small); 28 | } 29 | 30 | .widget-array__header { 31 | display: flex; 32 | align-items: center; 33 | justify-content: space-between; 34 | width: 100%; 35 | font-size: 0.8em; 36 | user-select: none; 37 | //// sl-icon::pa { 38 | //// height: 0.2rem; 39 | //// } 40 | //// display: none; 41 | 42 | sl-tag::part(base) { 43 | background: var(--sl-color-neutral-100); 44 | } 45 | } 46 | 47 | .widget-array__handle { 48 | display: flex; 49 | flex-grow: 1; 50 | align-items: center; 51 | justify-content: space-between; 52 | height: 2rem; 53 | padding-left: var(--sl-spacing-2x-small); 54 | margin: 0 var(--sl-spacing-medium) 0 0; 55 | font-size: 1.25em; 56 | color: var(--sl-color-neutral-500); 57 | // cursor: grab; 58 | cursor: move; 59 | transition: opacity, var(--sl-transition-fast); 60 | 61 | &:hover { 62 | color: var(--sl-color-neutral-600); 63 | background: var(--sl-color-neutral-100); 64 | border-radius: var(--sl-border-radius-x-large); 65 | transition: var(--sl-transition-medium); 66 | } 67 | 68 | &:active { 69 | // cursor: grabbing; 70 | user-select: none; 71 | } 72 | 73 | .widget-array__handle-grip { 74 | display: flex; 75 | flex-grow: 1; 76 | justify-content: center; 77 | } 78 | } 79 | 80 | .widget-array__add-zone { 81 | display: flex; 82 | padding: var(--sl-spacing-2x-large) var(--sl-spacing-2x-large); 83 | border: 2px dashed var(--sl-color-gray-100); 84 | border-radius: var(--sl-border-radius-large); 85 | box-shadow: var(--sl-shadow-large) inset; 86 | 87 | & > sl-button { 88 | width: 100%; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/button-group-boolean.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/radio-group/radio-group.js'; 7 | import '@shoelace-style/shoelace/dist/components/radio-button/radio-button.js'; 8 | import type { SlRadioGroup } from '@shoelace-style/shoelace'; 9 | 10 | export const buttonGroupBoolean: Widgets['buttonGroupBoolean'] = ( 11 | options, 12 | ) => html` 13 | { 21 | const newValue = (event.target as SlRadioGroup).value; 22 | options.valueChangedCallback?.( 23 | newValue === 'true' ?? newValue !== 'false' ?? undefined, 24 | ); 25 | }} 26 | > 27 | ${options.trueLabel ?? 'Yes'} 35 | ${options.falseLabel ?? 'No'} 40 | 41 | `; 42 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/button-group.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/radio-group/radio-group.js'; 7 | import '@shoelace-style/shoelace/dist/components/radio-button/radio-button.js'; 8 | import type { SlRadioGroup } from '@shoelace-style/shoelace'; 9 | 10 | export const buttonGroup: Widgets['buttonGroup'] = (options) => html` 11 | { 19 | let newValue: (typeof options)['enum'][number] = ( 20 | event.target as SlRadioGroup 21 | ).value; 22 | 23 | if (options.type === 'number') { 24 | newValue = Number(newValue); 25 | } 26 | 27 | options.valueChangedCallback?.(newValue); 28 | }} 29 | > 30 | ${options.enum?.map( 31 | (enumVal) => 32 | html`${enumVal}`, 40 | )} 41 | 42 | `; 43 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/callout.scss: -------------------------------------------------------------------------------- 1 | // NOTE: Extracted from Shoelace docs website 2 | 3 | /* Callouts */ 4 | .theme-shoelace.widget-callout { 5 | position: relative; 6 | padding: 1rem 1.5rem 1rem 2rem; 7 | margin: var(--sl-spacing-x-large) var(--sl-spacing-large); 8 | color: var(--sl-color-neutral-800); 9 | background-color: var(--sl-color-neutral-100); 10 | border-left: solid 4px var(--sl-color-neutral-500); 11 | border-radius: var(--sl-border-radius-medium); 12 | 13 | & > :first-child { 14 | margin-top: 0; 15 | } 16 | 17 | & > :last-child { 18 | margin-bottom: 0; 19 | } 20 | 21 | &.callout--tip { 22 | color: var(--sl-color-primary-800); 23 | background-color: var(--sl-color-primary-100); 24 | border-left-color: var(--sl-color-primary-600); 25 | } 26 | 27 | &::before { 28 | position: absolute; 29 | top: calc(50% - 0.8rem); 30 | left: calc(-0.8rem - 2px); 31 | display: flex; 32 | align-items: center; 33 | justify-content: center; 34 | width: 1.6rem; 35 | height: 1.6rem; 36 | clip-path: circle(50% at 50% 50%); 37 | font-family: var(--sl-font-serif); 38 | font-weight: var(--sl-font-weight-bold); 39 | color: var(--sl-color-neutral-0); 40 | content: ''; 41 | } 42 | 43 | &.callout--tip::before { 44 | font-style: italic; 45 | content: 'i'; 46 | background-color: var(--sl-color-primary-600); 47 | } 48 | 49 | &.callout--warning { 50 | color: var(--sl-color-warning-800); 51 | background-color: var(--sl-color-warning-100); 52 | border-left-color: var(--sl-color-warning-600); 53 | } 54 | 55 | &.callout--warning::before { 56 | content: '!'; 57 | background-color: var(--sl-color-warning-600); 58 | } 59 | 60 | &.callout--danger { 61 | color: var(--sl-color-danger-800); 62 | background-color: var(--sl-color-danger-100); 63 | border-left-color: var(--sl-color-danger-600); 64 | } 65 | 66 | &.callout--danger::before { 67 | content: '‼'; 68 | background-color: var(--sl-color-danger-600); 69 | } 70 | 71 | & + & { 72 | margin-top: calc(-0.5 * var(--sl-spacing-medium)); 73 | } 74 | 75 | a { 76 | color: inherit; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/callout.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { classMap } from 'lit/directives/class-map.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | export const callout: Widgets['callout'] = (options) => { 7 | console.warn(options.message); 8 | return html` 9 | 19 | `; 20 | }; 21 | 22 | export const flag = (feature: string) => { 23 | const message = `\`${feature}\` is not supported yet, but you can preview it with \`experimental = { ${feature}: true, ... }\``; 24 | console.error(message); 25 | return html` 26 | 35 | `; 36 | }; 37 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/checkbox-group.scss: -------------------------------------------------------------------------------- 1 | // TODO: 2 | 3 | .theme-shoelace.widget-checkbox-group { 4 | .help-text { 5 | font-size: var(--sl-input-help-text-font-size-medium); 6 | color: var(--sl-input-help-text-color); 7 | } 8 | 9 | .widget-checkbox-group__list { 10 | display: flex; 11 | flex-wrap: wrap; 12 | gap: var(--sl-spacing-large) var(--sl-spacing-4x-large); 13 | justify-content: space-evenly; 14 | padding: var(--sl-spacing-medium) var(--sl-spacing-x-large); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/checkbox-group.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js'; 6 | import type { SlCheckbox } from '@shoelace-style/shoelace'; 7 | 8 | export const checkboxGroup: Widgets['checkboxGroup'] = (options) => html` 9 |
14 | ${options.label} 15 | ${options.helpText 16 | ? html`
${options.helpText}
` 17 | : nothing} 18 | 19 | 20 | 21 | 22 |
23 | ${options?.enum?.map((enumValue) => { 24 | return html` v === enumValue) ?? false} 26 | @sl-change=${(event: CustomEvent) => { 27 | const { checked } = event.target as SlCheckbox; 28 | 29 | const newData: string | number[] = []; 30 | 31 | options?.enum?.forEach((eVal) => { 32 | if (eVal === enumValue && checked) { 33 | newData.push(eVal); 34 | } 35 | }); 36 | options.value?.forEach((dVal) => { 37 | if (dVal !== enumValue) { 38 | newData.push(dVal); 39 | } 40 | }); 41 | 42 | options.valueChangedCallback?.(newData); 43 | }} 44 | .disabled=${options.disabled} 45 | >${enumValue}`; 47 | })} 48 |
49 |
50 | `; 51 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/checkbox.scss: -------------------------------------------------------------------------------- 1 | // TODO: 2 | 3 | .theme-shoelace.widget-checkbox { 4 | display: flex; 5 | flex-wrap: wrap; 6 | gap: var(--sl-spacing-small); 7 | 8 | label { 9 | cursor: pointer; 10 | } 11 | 12 | .widget-checkbox__description { 13 | width: 100%; 14 | font-size: var(--sl-input-help-text-font-size-medium); 15 | color: var(--sl-input-help-text-color); 16 | } 17 | 18 | .widget-checkbox__label { 19 | width: 100%; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/checkbox.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 7 | import type { SlCheckbox } from '@shoelace-style/shoelace'; 8 | 9 | export const checkbox: Widgets['checkbox'] = (options) => html` 10 |
11 | { 18 | const { checked: newValue } = event.target as SlCheckbox; 19 | options.valueChangedCallback?.(newValue); 20 | }} 21 | .disabled=${options.disabled} 22 | > 23 | 24 | 25 | 26 | ${options.helpText 27 | ? html`
28 | ${options.helpText} 29 |
` 30 | : nothing} 31 |
32 | `; 33 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/color-picker.scss: -------------------------------------------------------------------------------- 1 | .theme-shoelace.widget-color-picker { 2 | display: flex; 3 | flex-wrap: wrap; 4 | gap: var(--sl-spacing-large); 5 | align-items: center; 6 | // padding: 0.25rem 0; 7 | 8 | .widget-color-picker__description { 9 | // padding-top: 0.25rem; 10 | // width: 100%; 11 | font-size: var(--sl-input-help-text-font-size-medium); 12 | color: var(--sl-input-help-text-color); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/color-picker.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@shoelace-style/shoelace/dist/components/color-picker/color-picker.js'; 6 | import type { SlColorPicker } from '@shoelace-style/shoelace'; 7 | 8 | export const colorPicker: Widgets['colorPicker'] = (options) => html` 9 |
10 | 11 | { 15 | const newValue = (event.target as SlColorPicker).value; 16 | 17 | options.valueChangedCallback?.(newValue); 18 | }} 19 | .disabled=${options.disabled} 20 | > 21 | 22 |
${options.helpText}
23 |
24 | `; 25 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/date.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 7 | import type { SlInput } from '@shoelace-style/shoelace'; 8 | 9 | export const date: Widgets['date'] = (options) => html` 10 | { 19 | let { value } = event.target as SlInput; 20 | 21 | if (options.type === 'datetime-local') { 22 | value = new Date(value); 23 | } 24 | 25 | options.valueChangedCallback?.( 26 | // NOTE: Date time does not return `valueAsDate` 27 | // TODO: valueChangedCallback should coerce to Date later (when possible) 28 | value, 29 | ); 30 | }} 31 | .disabled=${options.disabled} 32 | .readonly=${options.readonly} 33 | > 34 | 35 | `; 36 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/index.ts: -------------------------------------------------------------------------------- 1 | // import './defs/range.def.js'; 2 | 3 | // keep-sorted start 4 | export { array } from './array.js'; 5 | export { buttonGroup } from './button-group.js'; 6 | export { buttonGroupBoolean } from './button-group-boolean.js'; 7 | export { callout } from './callout.js'; 8 | export { checkbox } from './checkbox.js'; 9 | export { checkboxGroup } from './checkbox-group.js'; 10 | export { colorPicker } from './color-picker.js'; 11 | export { date } from './date.js'; 12 | export { number } from './number.js'; 13 | export { object } from './object.js'; 14 | export { radioGroup } from './radio-group.js'; 15 | export { radioGroupBoolean } from './radio-group-boolean.js'; 16 | export { range } from './range.js'; 17 | export { rating } from './rating.js'; 18 | export { select } from './select.js'; 19 | export { selectMultiple } from './select-multiple.js'; 20 | export { submit } from './submit.js'; 21 | export { switchh as switch } from './switch.js'; 22 | export { text } from './text.js'; 23 | export { textarea } from './textarea.js'; 24 | // keep-sorted end 25 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/number.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 7 | import type { SlInput } from '@shoelace-style/shoelace'; 8 | 9 | export const number: Widgets['number'] = (options) => 10 | html` 11 | { 24 | const newValue = (event.target as SlInput).valueAsNumber; 25 | console.log(newValue); 26 | options.valueChangedCallback?.(newValue); 27 | }} 28 | .disabled=${options.disabled} 29 | .readonly=${options.readonly} 30 | >`; 31 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/object.scss: -------------------------------------------------------------------------------- 1 | .theme-shoelace.widget-object { 2 | } 3 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/object.ts: -------------------------------------------------------------------------------- 1 | import { nothing, html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | export const object: Widgets['object'] = (options) => html` 6 |
11 | ${options.label ? html`${options.label}` : ``} 12 | 13 | ${options.helpText 14 | ? html`
15 | ${options.helpText} 16 |
` 17 | : nothing} 18 | 19 | ${options.children} 20 |
21 | `; 22 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/radio-group-boolean.scss: -------------------------------------------------------------------------------- 1 | .theme-shoelace.widget-radio-group-boolean { 2 | &::part(form-control-input) { 3 | display: flex; 4 | gap: 1rem; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/radio-group-boolean.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/radio-group/radio-group.js'; 7 | import '@shoelace-style/shoelace/dist/components/radio/radio.js'; 8 | import type { SlRadioGroup } from '@shoelace-style/shoelace'; 9 | 10 | export const radioGroupBoolean: Widgets['radioGroupBoolean'] = ( 11 | options, 12 | ) => html` 13 | 14 | { 23 | const newValue = (event.target as SlRadioGroup).value; 24 | options.valueChangedCallback?.( 25 | newValue === 'true' ?? newValue !== 'false' ?? undefined, 26 | ); 27 | }} 28 | > 29 | ${options.trueLabel ?? 'Yes'} 32 | ${options.falseLabel ?? 'No'} 35 | 36 | `; 37 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/radio-group.scss: -------------------------------------------------------------------------------- 1 | .theme-shoelace.widget-radio-group { 2 | &::part(form-control-input) { 3 | display: flex; 4 | flex-wrap: wrap; 5 | gap: var(--sl-spacing-small) var(--sl-spacing-x-large); 6 | // justify-content: space-evenly; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/radio-group.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/radio-group/radio-group.js'; 7 | import '@shoelace-style/shoelace/dist/components/radio/radio.js'; 8 | import type { SlRadioGroup } from '@shoelace-style/shoelace'; 9 | 10 | export const radioGroup: Widgets['radioGroup'] = (options) => html` 11 | { 20 | let newValue: (typeof options)['enum'][number] = ( 21 | event.target as SlRadioGroup 22 | ).value; 23 | 24 | if (options.type === 'number') { 25 | newValue = Number(newValue); 26 | } 27 | 28 | options.valueChangedCallback?.(newValue); 29 | }} 30 | > 31 | ${options.enum?.map( 32 | (enumVal) => 33 | html`${enumVal}`, 36 | )} 37 | 38 | `; 39 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/range.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/range/range.js'; 7 | import type { SlRange } from '@shoelace-style/shoelace'; 8 | 9 | export const range: Widgets['number'] = (options) => 10 | html` 11 | { 23 | const newValue = (event.target as SlRange).value; 24 | console.log(newValue); 25 | options.valueChangedCallback?.(newValue); 26 | }} 27 | .disabled=${options.disabled} 28 | >`; 29 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/rating.scss: -------------------------------------------------------------------------------- 1 | .theme-shoelace.widget-rating { 2 | label { 3 | display: block; 4 | margin-bottom: 0.5em; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/rating.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/rating/rating.js'; 7 | import type { SlRating } from '@shoelace-style/shoelace'; 8 | 9 | export const rating: Widgets['number'] = (options) => 10 | html` 11 |
12 | 13 | 14 | { 27 | const newValue = (event.target as SlRating).value; 28 | console.log(newValue); 29 | options.valueChangedCallback?.(newValue); 30 | }} 31 | .disabled=${options.disabled} 32 | .readonly=${options.readonly} 33 | > 34 |
`; 35 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/select-multiple.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@shoelace-style/shoelace/dist/components/select/select.js'; 6 | import type { SlSelect, SlSelectEvent } from '@shoelace-style/shoelace'; 7 | 8 | export const selectMultiple: Widgets['selectMultiple'] = (options) => html` 9 | { 20 | const { value } = event.target as SlSelect; 21 | 22 | options.valueChangedCallback?.(value); 23 | }} 24 | > 25 | ${options?.enum?.map((enumValue) => { 26 | return html`${enumValue}`; 27 | })} 28 | 29 | `; 30 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/select.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/select/select.js'; 7 | import '@shoelace-style/shoelace/dist/components/option/option.js'; 8 | import type { SlSelect } from '@shoelace-style/shoelace'; 9 | 10 | export const select: Widgets['select'] = (options) => html` 11 | { 15 | let newValue: string | null | number | string[] = ( 16 | event.target as SlSelect 17 | ).value; 18 | if (Array.isArray(newValue)) return; 19 | if (options.type === 'number' || options.type === 'integer') { 20 | newValue = Number(newValue); 21 | } 22 | options.valueChangedCallback?.(newValue); 23 | }} 24 | .disabled=${options.disabled} 25 | label=${ifDefined(options.label)} 26 | .helpText=${options.helpText ?? ''} 27 | >${options.enum?.map( 28 | (enumValue) => 29 | html` 30 | ${enumValue} 31 | `, 32 | )} 34 | `; 35 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/submit.scss: -------------------------------------------------------------------------------- 1 | .theme-shoelace.widget-submit { 2 | display: flex; 3 | justify-content: center; 4 | margin: var(--sl-spacing-2x-large) 0; 5 | } 6 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/submit.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@shoelace-style/shoelace/dist/components/button/button.js'; 6 | 7 | export const submit: Widgets['submit'] = (options) => html` 8 | 9 |
10 | ${options.label ?? 'Submit'} 13 |
14 | `; 15 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/switch.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/switch/switch.js'; 7 | import type { SlSwitch } from '@shoelace-style/shoelace'; 8 | 9 | export const switchh: Widgets['switch'] = (options) => html` 10 | { 17 | const { checked: newValue } = event.target as SlSwitch; 18 | options.valueChangedCallback?.(newValue); 19 | }} 20 | .disabled=${options.disabled} 21 | >${options.label} 22 | ${options.helpText 23 | ? html` - ${options.helpText}` 24 | : nothing} 25 | 26 | `; 27 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/text.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/input/input.js'; 7 | import type { SlInput } from '@shoelace-style/shoelace'; 8 | 9 | export const text: Widgets['text'] = (options) => html` 10 | 11 | { 24 | const { value: newValue } = event.target as SlInput; 25 | options.valueChangedCallback?.(newValue); 26 | }} 27 | .disabled=${options.disabled} 28 | .readonly=${options.readonly} 29 | > 30 | 31 | `; 32 | -------------------------------------------------------------------------------- /packages/shoelace/src/widgets/textarea.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@shoelace-style/shoelace/dist/components/textarea/textarea.js'; 7 | import type { SlTextarea } from '@shoelace-style/shoelace'; 8 | 9 | export const textarea: Widgets['textarea'] = (options) => html` 10 | 11 | { 22 | const { value: newValue } = event.target as SlTextarea; 23 | options.valueChangedCallback?.(newValue); 24 | }} 25 | .disabled=${options.disabled} 26 | .readonly=${options.readonly} 27 | > 28 | 29 | `; 30 | -------------------------------------------------------------------------------- /packages/shoelace/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "outDir": "dist/esm", 22 | "declaration": true, 23 | 24 | "removeComments": true, 25 | 26 | "noImplicitAny": true, 27 | 28 | "declarationMap": true 29 | }, 30 | "include": [ 31 | // 32 | "src/index.ts" 33 | ], 34 | "exclude": ["src/styles.ts", "src/react.ts"] 35 | } 36 | -------------------------------------------------------------------------------- /packages/spectrum/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.2.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/spectrum@0.1.2...@jsfe/spectrum@0.2.0) (2024-06-06) 7 | 8 | ### Bug Fixes 9 | 10 | - add types field to package.json ([#8](https://github.com/json-schema-form-element/jsfe/issues/8)) ([e0ecf92](https://github.com/json-schema-form-element/jsfe/commit/e0ecf923475ca123ea9ee76d331d35d68c591102)) 11 | 12 | ### Features 13 | 14 | - submit button custom text ([e751828](https://github.com/json-schema-form-element/jsfe/commit/e751828b3bc07d21fc537b97e1b531a94a77fd70)), closes [#7](https://github.com/json-schema-form-element/jsfe/issues/7) 15 | 16 | ### Reverts 17 | 18 | - "style: bulk reformat with new imports orders" ([1236078](https://github.com/json-schema-form-element/jsfe/commit/12360786626927f2c8a0273b245b906e5946b920)) 19 | 20 | ## [0.1.2](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/spectrum@0.1.1...@jsfe/spectrum@0.1.2) (2023-11-30) 21 | 22 | **Note:** Version bump only for package @jsfe/spectrum 23 | 24 | ## [0.1.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/spectrum@0.1.0...@jsfe/spectrum@0.1.1) (2023-11-30) 25 | 26 | **Note:** Version bump only for package @jsfe/spectrum 27 | 28 | # 0.1.0 (2023-11-30) 29 | 30 | ### Features 31 | 32 | - initialize adobe spectrum package 🆕 ([2b8c8b5](https://github.com/json-schema-form-element/jsfe/commit/2b8c8b5cf9f0e712c57fc9c2672121fcbefa8656)) 33 | -------------------------------------------------------------------------------- /packages/spectrum/README.md: -------------------------------------------------------------------------------- 1 | # JSON Schema Form Element — ***Spectrum*** edition 2 | 3 | ```sh 4 | npm install @jsfe/spectrum 5 | ``` 6 | 7 | - Consult the [documentation](../../README.md). 8 | - Open the [playground](https://jsfe.js.org). 9 | - Try the [examples](https://github.com/json-schema-form-element/examples#readme). 10 | 11 | --- 12 | 13 | # `packages/spectrum/src/form.def.ts`: 14 | 15 | ## Exports 16 | 17 | | Kind | Name | Declaration | Module | Package | 18 | | --------------------------- | -------------- | ----------- | ------------------------------ | ------- | 19 | | `custom-element-definition` | `jsf-spectrum` | JsfSpectrum | /packages/spectrum/src/form.js | | 20 | 21 | # `packages/spectrum/src/form.ts`: 22 | 23 | ## class: `JsfSpectrum`, `jsf-spectrum` 24 | 25 | ### Superclass 26 | 27 | | Name | Module | Package | 28 | | ----- | ------ | ---------- | 29 | | `Jsf` | | @jsfe/form | 30 | 31 | ### Fields 32 | 33 | | Name | Privacy | Type | Default | Description | Inherited From | 34 | | ------------- | ------- | ------- | ---------- | ----------- | -------------- | 35 | | `widgets` | public | | `widgets` | | | 36 | | `styleSheets` | public | `array` | `[styles]` | | | 37 | 38 |
39 | 40 | ## Exports 41 | 42 | | Kind | Name | Declaration | Module | Package | 43 | | ---- | ------------- | ----------- | ----------------------------- | ------- | 44 | | `js` | `JsfSpectrum` | JsfSpectrum | packages/spectrum/src/form.ts | | 45 | 46 | # `packages/spectrum/src/index.ts`: 47 | 48 | ## Exports 49 | 50 | | Kind | Name | Declaration | Module | Package | 51 | | ---- | ------------- | ----------- | --------- | ------------------ | 52 | | `js` | `JsfSpectrum` | JsfSpectrum | ./form.js | | 53 | | `js` | `*` | \* | | ./widgets/index.js | 54 | | `js` | `*` | \* | | @jsfe/types | 55 | | `js` | `Jsf` | Jsf | | @jsfe/form | 56 | 57 | # `packages/spectrum/src/styles.ts`: 58 | 59 | ## Variables 60 | 61 | | Name | Description | Type | 62 | | -------- | ----------- | ---- | 63 | | `styles` | | | 64 | 65 |
66 | 67 | ## Exports 68 | 69 | | Kind | Name | Declaration | Module | Package | 70 | | ---- | -------- | ----------- | ------------------------------- | ------- | 71 | | `js` | `styles` | styles | packages/spectrum/src/styles.ts | | 72 | 73 | -------------------------------------------------------------------------------- /packages/spectrum/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/spectrum", 3 | "version": "0.2.0", 4 | "description": "Spectrum v0 forms, auto-generated by JSON schemas.", 5 | "keywords": [ 6 | "json-schema", 7 | "spectrum", 8 | "forms", 9 | "generation", 10 | "declarative", 11 | "openapi", 12 | "mongodb", 13 | "page-builder" 14 | ], 15 | "homepage": "https://jsfe.js.org", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/json-schema-form-element/jsfe", 19 | "directory": "packages/spectrum" 20 | }, 21 | "license": "ISC", 22 | "author": { 23 | "name": "Julian Cataldo", 24 | "email": "contact@juliancataldo.com", 25 | "url": "https://www.juliancataldo.com" 26 | }, 27 | "type": "module", 28 | "types": "./dist/esm/index.d.ts", 29 | "exports": { 30 | ".": "./dist/esm/index.js", 31 | "./scss": "./src/styles.scss", 32 | "./scss/*": "./src/widgets/*.scss", 33 | "./css": "./dist/esm/styles.css", 34 | "./jss": "./dist/esm/styles.js", 35 | "./min": "./dist/esm-min" 36 | }, 37 | "files": [ 38 | "./dist/esm", 39 | "./dist/esm-min", 40 | "./src/**/*.scss", 41 | "./vscode.html-custom-data.json", 42 | "./vscode.css-custom-data.json", 43 | "./custom-elements.json" 44 | ], 45 | "scripts": { 46 | "build": "pnpm clean ; pnpm ts:build ; pnpm css:build ; pnpm css:to-js", 47 | "clean": "rm -rf ./dist", 48 | "css:build": "pnpm sass --no-source-map src/styles.scss:dist/esm/styles.css", 49 | "css:dev": "pnpm sass --watch src/styles.scss:dist/esm/styles.css & pnpm css:to-js:dev", 50 | "css:to-js": "node ../../scripts/css-to-js.js dist/esm/styles.css", 51 | "css:to-js:dev": "nodemon dist/esm/styles.css -x 'pnpm css:to-js'", 52 | "dev": "pnpm ts:dev & pnpm css:dev & (sleep 3 && pnpm css:to-js:dev)", 53 | "ts:build": "pnpm tsc", 54 | "ts:dev": "pnpm tsc --watch" 55 | }, 56 | "dependencies": { 57 | "@jsfe/form": "workspace:*", 58 | "@jsfe/types": "workspace:*" 59 | }, 60 | "devDependencies": { 61 | "@spectrum-web-components/styles": "^0.40.0", 62 | "sass": "^1.69.5", 63 | "typescript": "^5.3.2" 64 | }, 65 | "peerDependencies": { 66 | "@spectrum-web-components/button": "^0.40.0", 67 | "@spectrum-web-components/field-label": "^0.39.1", 68 | "@spectrum-web-components/help-text": "^0.40.0", 69 | "@spectrum-web-components/icon": "^0.39.1", 70 | "@spectrum-web-components/icons": "^0.39.1", 71 | "@spectrum-web-components/number-field": "^0.39.1", 72 | "@spectrum-web-components/radio": "^0.40.0", 73 | "@spectrum-web-components/slider": "^0.40.0", 74 | "@spectrum-web-components/switch": "^0.40.0", 75 | "@spectrum-web-components/textfield": "^0.39.1", 76 | "@spectrum-web-components/theme": "^0.39.1", 77 | "lit": "^3.1.0" 78 | }, 79 | "publishConfig": { 80 | "access": "public" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/spectrum/src/form.def.ts: -------------------------------------------------------------------------------- 1 | import { JsfSpectrum } from './form.js'; 2 | 3 | customElements.define('jsf-spectrum', JsfSpectrum); 4 | 5 | declare global { 6 | interface HTMLElementTagNameMap { 7 | 'jsf-spectrum': JsfSpectrum; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/spectrum/src/form.ts: -------------------------------------------------------------------------------- 1 | import { Jsf } from '@jsfe/form'; 2 | import * as widgets from './widgets/index.js'; 3 | import { styles } from './styles.js'; 4 | 5 | export class JsfSpectrum extends Jsf { 6 | public widgets = widgets; 7 | 8 | public styleSheets = [styles]; 9 | } 10 | -------------------------------------------------------------------------------- /packages/spectrum/src/index.ts: -------------------------------------------------------------------------------- 1 | import './form.def.js'; 2 | 3 | export { JsfSpectrum } from './form.js'; 4 | export * as widgets from './widgets/index.js'; 5 | 6 | export type * from '@jsfe/types'; 7 | export type { Jsf } from '@jsfe/form'; 8 | -------------------------------------------------------------------------------- /packages/spectrum/src/styles.scss: -------------------------------------------------------------------------------- 1 | // keep-sorted start 2 | // @import './widgets/_all.scss'; 3 | @import './widgets/_fieldset.scss'; 4 | // @import './widgets/array.scss'; 5 | // @import './widgets/callout.scss'; 6 | // @import './widgets/checkbox-group.scss'; 7 | // @import './widgets/checkbox.scss'; 8 | // @import './widgets/color-picker.scss'; 9 | // @import './widgets/object.scss'; 10 | // @import './widgets/radio-group-boolean.scss'; 11 | // @import './widgets/radio-group.scss'; 12 | // @import './widgets/rating.scss'; 13 | @import './widgets/submit.scss'; 14 | @import './widgets/text.scss'; 15 | // keep-sorted end 16 | 17 | // - 18 | // NOTE: For tokens, SEE: 19 | 20 | // ./node_modules/@spectrum-web-components/styles/spectrum-scale-large.css 21 | // ./node_modules/@spectrum-web-components/styles/spectrum-theme-dark.css 22 | // ./node_modules/@spectrum-web-components/styles/typography.css 23 | // ./node_modules/@spectrum-web-components/styles/core-global.css 24 | // ./node_modules/@spectrum-web-components/styles/spectrum-core-global.cs 25 | -------------------------------------------------------------------------------- /packages/spectrum/src/styles.ts: -------------------------------------------------------------------------------- 1 | /* STUB */ 2 | 3 | import { css } from 'lit'; 4 | 5 | export const styles = css` 6 | /* STUB - Compiled SCSS goes here */ 7 | `; 8 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/_fieldset.scss: -------------------------------------------------------------------------------- 1 | // WIP 2 | 3 | .theme-spectrum.widget-fieldset { 4 | display: flex; 5 | flex-direction: column; 6 | gap: var(--spectrum-global-dimension-size-600) 0; 7 | padding: var(--sp-spacing-medium) var(--sp-spacing-small); 8 | margin: 0; 9 | font-weight: var(--sp-font-weight-semibold); 10 | border: 1px solid var(--sp-color-neutral-50); 11 | border-radius: var(--sp-border-radius-large); 12 | 13 | .widget-fieldset__description { 14 | font-size: var(--sp-input-help-text-font-size-medium); 15 | color: var(--sp-input-help-text-color); 16 | } 17 | 18 | & & { 19 | transition: box-shadow var(--sp-transition-medium); 20 | 21 | &:hover { 22 | // background: var(--sp-color-gray-50); 23 | box-shadow: var(--sp-shadow-medium); 24 | } 25 | } 26 | 27 | legend { 28 | // font-size: var(--sp-font-size-large); 29 | } 30 | 31 | &.level-0 { 32 | legend { 33 | font-size: var(--sp-font-size-2x-large); 34 | font-weight: 200; 35 | } 36 | } 37 | 38 | &.level-1 { 39 | legend { 40 | font-size: var(--sp-font-size-x-large); 41 | font-weight: var(--sp-font-weight-light); 42 | } 43 | } 44 | 45 | &.level-2 { 46 | legend { 47 | font-size: var(--sp-font-size-large); 48 | font-weight: var(--sp-font-weight-light); 49 | } 50 | } 51 | 52 | &.level-3 { 53 | legend { 54 | font-size: var(--sp-font-size-medium); 55 | font-weight: var(--sp-font-weight-bold); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/index.ts: -------------------------------------------------------------------------------- 1 | // import './defs/range.def.js'; 2 | 3 | // import '@spectrum-web-components/icon/sp-icon.js'; 4 | // import '@spectrum-web-components/icons/sp-icons-large.js'; 5 | // import '@spectrum-web-components/icons/sp-icons-medium.js'; 6 | 7 | // keep-sorted start 8 | // export { array } from './array.js'; 9 | // export { buttonGroup } from './button-group.js'; 10 | // export { buttonGroupBoolean } from './button-group-boolean.js'; 11 | // export { callout } from './callout.js'; 12 | // export { checkbox } from './checkbox.js'; 13 | // export { checkboxGroup } from './checkbox-group.js'; 14 | // export { colorPicker } from './color-picker.js'; 15 | // export { date } from './date.js'; 16 | export { number } from './number.js'; 17 | export { object } from './object.js'; 18 | export { radioGroup } from './radio-group.js'; 19 | // export { radioGroupBoolean } from './radio-group-boolean.js'; 20 | export { range } from './range.js'; 21 | // export { rating } from './rating.js'; 22 | // export { select } from './select.js'; 23 | // export { selectMultiple } from './select-multiple.js'; 24 | export { submit } from './submit.js'; 25 | export { switchh as switch } from './switch.js'; 26 | export { text } from './text.js'; 27 | export { textarea } from './textarea.js'; 28 | // keep-sorted end 29 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/number.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@spectrum-web-components/field-label/sp-field-label.js'; 7 | import '@spectrum-web-components/number-field/sp-number-field.js'; 8 | import '@spectrum-web-components/help-text/sp-help-text.js'; 9 | 10 | import type { NumberField } from '@spectrum-web-components/number-field'; 11 | 12 | export const number: Widgets['number'] = (options) => 13 | html` 14 | { 26 | const newValue = (event.target as NumberField).valueAsNumber; 27 | console.log(newValue); 28 | options.valueChangedCallback?.(newValue); 29 | }} 30 | >`; 31 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/object.scss: -------------------------------------------------------------------------------- 1 | .theme-spectrum.widget-object { 2 | } 3 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/object.ts: -------------------------------------------------------------------------------- 1 | import { nothing, html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | export const object: Widgets['object'] = (options) => html` 6 |
11 | ${options.label ? html`${options.label}` : ``} 12 | 13 | ${options.helpText 14 | ? html`
15 | ${options.helpText} 16 |
` 17 | : nothing} 18 | 19 | ${options.children} 20 |
21 | `; 22 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/radio-group.scss: -------------------------------------------------------------------------------- 1 | .theme-spectrum.widget-radio-group { 2 | &::part(form-control-input) { 3 | display: flex; 4 | flex-wrap: wrap; 5 | gap: var(--sl-spacing-small) var(--sl-spacing-x-large); 6 | // justify-content: space-evenly; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/radio-group.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@spectrum-web-components/radio/sp-radio-group.js'; 7 | import '@spectrum-web-components/radio/sp-radio.js'; 8 | 9 | import type { RadioGroup } from '@spectrum-web-components/radio'; 10 | 11 | export const radioGroup: Widgets['radioGroup'] = (options) => html` 12 | { 21 | let newValue: (typeof options)['enum'][number] = ( 22 | event.target as RadioGroup 23 | ).value; 24 | 25 | if (options.type === 'number') { 26 | newValue = Number(newValue); 27 | } 28 | 29 | options.valueChangedCallback?.(newValue); 30 | }} 31 | > 32 | ${options.enum?.map( 33 | (enumVal) => 34 | html`${enumVal}`, 35 | )} 36 | 37 | `; 38 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/range.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@spectrum-web-components/slider/sp-slider.js'; 7 | import type { Slider } from '@spectrum-web-components/slider'; 8 | 9 | export const range: Widgets['number'] = (options) => 10 | html` 11 | { 23 | const newValue = (event.target as Slider).value; 24 | console.log(newValue); 25 | options.valueChangedCallback?.(newValue); 26 | }} 27 | >`; 28 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/submit.scss: -------------------------------------------------------------------------------- 1 | .theme-spectrum.widget-submit { 2 | display: flex; 3 | justify-content: center; 4 | margin: var(--sl-spacing-2x-large) 0; 5 | } 6 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/submit.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import '@spectrum-web-components/button/sp-button.js'; 6 | 7 | export const submit: Widgets['submit'] = (options) => html` 8 | 9 |
10 | ${options.label ?? 'Submit'} 13 |
14 | `; 15 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/switch.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@spectrum-web-components/switch/sp-switch.js'; 7 | import type { Switch } from '@spectrum-web-components/switch'; 8 | 9 | export const switchh: Widgets['switch'] = (options) => html` 10 | { 17 | const { checked: newValue } = event.target as Switch; 18 | options.valueChangedCallback?.(newValue); 19 | }} 20 | >${options.label} 21 | ${options.helpText 22 | ? html` - ${options.helpText}` 23 | : nothing} 24 | 25 | `; 26 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/text.scss: -------------------------------------------------------------------------------- 1 | sp-textfield { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/text.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import '@spectrum-web-components/textfield/sp-textfield.js'; 7 | import '@spectrum-web-components/field-label/sp-field-label.js'; 8 | import '@spectrum-web-components/help-text/sp-help-text.js'; 9 | 10 | import type { Textfield } from '@spectrum-web-components/textfield'; 11 | 12 | export const text: Widgets['text'] = (options) => html` 13 |
14 | ${options.label 15 | ? html`${options.label}` 18 | : nothing} 19 | { 30 | const { value: newValue } = event.target as Textfield; 31 | options.valueChangedCallback?.(newValue); 32 | }} 33 | > 34 | 35 | 36 | ${options.helpText 37 | ? html` 38 | ${options.helpText} 41 | ` 42 | : nothing} 43 |
44 | `; 45 | -------------------------------------------------------------------------------- /packages/spectrum/src/widgets/textarea.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import type { Textfield } from '@spectrum-web-components/textfield'; 7 | 8 | export const textarea: Widgets['text'] = (options) => html` 9 | ${options.label 10 | ? html`${options.label}` 13 | : nothing} 14 | { 26 | const { value: newValue } = event.target as Textfield; 27 | options.valueChangedCallback?.(newValue); 28 | }} 29 | > 30 | 31 | 32 | `; 33 | -------------------------------------------------------------------------------- /packages/spectrum/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "outDir": "dist/esm", 22 | "declaration": true, 23 | 24 | "removeComments": true, 25 | 26 | "noImplicitAny": true, 27 | 28 | "declarationMap": true 29 | }, 30 | "include": [ 31 | // 32 | "src/index.ts" 33 | ], 34 | "exclude": ["src/styles.ts", "src/react.ts"] 35 | } 36 | -------------------------------------------------------------------------------- /packages/system/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.3.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/system@0.2.2...@jsfe/system@0.3.0) (2024-06-06) 7 | 8 | ### Bug Fixes 9 | 10 | - add types field to package.json ([#8](https://github.com/json-schema-form-element/jsfe/issues/8)) ([e0ecf92](https://github.com/json-schema-form-element/jsfe/commit/e0ecf923475ca123ea9ee76d331d35d68c591102)) 11 | - unwanted dependency ([9a71c28](https://github.com/json-schema-form-element/jsfe/commit/9a71c2809f298cc68420d6ef68c1e614ef06c921)) 12 | 13 | ### Features 14 | 15 | - submit button custom text ([e751828](https://github.com/json-schema-form-element/jsfe/commit/e751828b3bc07d21fc537b97e1b531a94a77fd70)), closes [#7](https://github.com/json-schema-form-element/jsfe/issues/7) 16 | 17 | ### Reverts 18 | 19 | - "style: bulk reformat with new imports orders" ([1236078](https://github.com/json-schema-form-element/jsfe/commit/12360786626927f2c8a0273b245b906e5946b920)) 20 | 21 | ## [0.2.2](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/system@0.2.1...@jsfe/system@0.2.2) (2023-11-30) 22 | 23 | **Note:** Version bump only for package @jsfe/system 24 | 25 | ## [0.2.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/system@0.2.0...@jsfe/system@0.2.1) (2023-11-30) 26 | 27 | **Note:** Version bump only for package @jsfe/system 28 | 29 | # [0.2.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/system@0.1.1...@jsfe/system@0.2.0) (2023-11-30) 30 | 31 | ### Features 32 | 33 | - more system / wired widgets, clean others ([bfcac92](https://github.com/json-schema-form-element/jsfe/commit/bfcac9247ded39af312b2df99a21a6d94d37c965)) 34 | 35 | ## [0.1.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/system@0.1.0...@jsfe/system@0.1.1) (2023-10-12) 36 | 37 | ### Bug Fixes 38 | 39 | - better styles, material export ([9a33cbb](https://github.com/json-schema-form-element/jsfe/commit/9a33cbb29059ac8827647db6a7deda45d9cb3c09)) 40 | 41 | # 0.1.0 (2023-10-11) 42 | 43 | ### Features 44 | 45 | - add base packages files ([d8e42bd](https://github.com/json-schema-form-element/jsfe/commit/d8e42bdcda5f8af5e2728e1556946d333e7f59b5)) 46 | - add components for all ui libraries packages ([7a6d3a5](https://github.com/json-schema-form-element/jsfe/commit/7a6d3a53f3939d00512c9f42925d1f9f1db246ff)) 47 | - re-organize to mono-repo ([3888c62](https://github.com/json-schema-form-element/jsfe/commit/3888c62a07b07aed2262c7e0c7b66919f30505ef)) 48 | - select multiple, typings fixes, refactor ([463aa85](https://github.com/json-schema-form-element/jsfe/commit/463aa85d7ba22480513bc485ab4ad849e39c5402)) 49 | -------------------------------------------------------------------------------- /packages/system/README.md: -------------------------------------------------------------------------------- 1 | # JSON Schema Form Element — ***System*** edition 2 | 3 | ```sh 4 | npm install @jsfe/system 5 | ``` 6 | 7 | - Consult the [documentation](../../README.md). 8 | - Open the [playground](https://jsfe.js.org). 9 | - Try the [examples](https://github.com/json-schema-form-element/examples#readme). 10 | 11 | --- 12 | 13 | # `packages/system/src/form.def.ts`: 14 | 15 | ## Exports 16 | 17 | | Kind | Name | Declaration | Module | Package | 18 | | --------------------------- | ------------ | ----------- | ---------------------------- | ------- | 19 | | `custom-element-definition` | `jsf-system` | JsfSystem | /packages/system/src/form.js | | 20 | 21 | # `packages/system/src/form.ts`: 22 | 23 | ## class: `JsfSystem`, `jsf-system` 24 | 25 | ### Superclass 26 | 27 | | Name | Module | Package | 28 | | ----- | ------ | ---------- | 29 | | `Jsf` | | @jsfe/form | 30 | 31 | ### Fields 32 | 33 | | Name | Privacy | Type | Default | Description | Inherited From | 34 | | ------------- | ------- | ------- | ---------- | ----------- | -------------- | 35 | | `widgets` | public | | `widgets` | | | 36 | | `styleSheets` | public | `array` | `[styles]` | | | 37 | 38 |
39 | 40 | ## Exports 41 | 42 | | Kind | Name | Declaration | Module | Package | 43 | | ---- | ----------- | ----------- | --------------------------- | ------- | 44 | | `js` | `JsfSystem` | JsfSystem | packages/system/src/form.ts | | 45 | 46 | # `packages/system/src/index.ts`: 47 | 48 | ## Exports 49 | 50 | | Kind | Name | Declaration | Module | Package | 51 | | ---- | ----------- | ----------- | --------- | ------------------ | 52 | | `js` | `JsfSystem` | JsfSystem | ./form.js | | 53 | | `js` | `*` | \* | | ./widgets/index.js | 54 | | `js` | `*` | \* | | @jsfe/types | 55 | | `js` | `Jsf` | Jsf | | @jsfe/form | 56 | 57 | # `packages/system/src/styles.ts`: 58 | 59 | ## Variables 60 | 61 | | Name | Description | Type | 62 | | -------- | ----------- | ---- | 63 | | `styles` | | | 64 | 65 |
66 | 67 | ## Exports 68 | 69 | | Kind | Name | Declaration | Module | Package | 70 | | ---- | -------- | ----------- | ----------------------------- | ------- | 71 | | `js` | `styles` | styles | packages/system/src/styles.ts | | 72 | 73 | -------------------------------------------------------------------------------- /packages/system/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/system", 3 | "version": "0.3.0", 4 | "description": "Raw system form controls auto-generated by JSON schemas.\nCan be used as a blueprint for your custom implementation.", 5 | "keywords": [ 6 | "json-schema", 7 | "raw", 8 | "forms", 9 | "generation", 10 | "declarative", 11 | "openapi", 12 | "mongodb", 13 | "page-builder" 14 | ], 15 | "homepage": "https://jsfe.js.org", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/json-schema-form-element/jsfe", 19 | "directory": "packages/system" 20 | }, 21 | "license": "ISC", 22 | "author": { 23 | "name": "Julian Cataldo", 24 | "email": "contact@juliancataldo.com", 25 | "url": "https://www.juliancataldo.com" 26 | }, 27 | "type": "module", 28 | "types": "./dist/esm/index.d.ts", 29 | "exports": { 30 | ".": "./dist/esm/index.js", 31 | "./scss": "./src/styles.scss", 32 | "./scss/*": "./src/widgets/*.scss", 33 | "./css": "./dist/esm/styles.css", 34 | "./jss": "./dist/esm/styles.js", 35 | "./min": "./dist/esm-min" 36 | }, 37 | "files": [ 38 | "./dist/esm", 39 | "./dist/esm-min", 40 | "./src/**/*.scss", 41 | "./vscode.html-custom-data.json", 42 | "./vscode.css-custom-data.json", 43 | "./custom-elements.json" 44 | ], 45 | "scripts": { 46 | "build": "pnpm clean ; pnpm ts:build ; pnpm css:build ; pnpm css:to-js", 47 | "clean": "rm -rf ./dist", 48 | "css:build": "pnpm sass --no-source-map src/styles.scss:dist/esm/styles.css", 49 | "css:dev": "pnpm sass --watch src/styles.scss:dist/esm/styles.css & pnpm css:to-js:dev", 50 | "css:to-js": "node ../../scripts/css-to-js.js dist/esm/styles.css", 51 | "css:to-js:dev": "nodemon dist/esm/styles.css -x 'pnpm css:to-js'", 52 | "dev": "pnpm ts:dev & pnpm css:dev & (sleep 3 && pnpm css:to-js:dev)", 53 | "ts:build": "pnpm tsc", 54 | "ts:dev": "pnpm tsc --watch" 55 | }, 56 | "dependencies": { 57 | "@jsfe/form": "workspace:*", 58 | "@jsfe/types": "workspace:*" 59 | }, 60 | "devDependencies": { 61 | "sass": "^1.69.5", 62 | "typescript": "^5.3.2" 63 | }, 64 | "peerDependencies": { 65 | "lit": "^3.1.0" 66 | }, 67 | "publishConfig": { 68 | "access": "public" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/system/src/form.def.ts: -------------------------------------------------------------------------------- 1 | import { JsfSystem } from './form.js'; 2 | 3 | customElements.define('jsf-system', JsfSystem); 4 | 5 | declare global { 6 | interface HTMLElementTagNameMap { 7 | 'jsf-system': JsfSystem; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/system/src/form.ts: -------------------------------------------------------------------------------- 1 | import { Jsf } from '@jsfe/form'; 2 | import * as widgets from './widgets/index.js'; 3 | import { styles } from './styles.js'; 4 | 5 | export class JsfSystem extends Jsf { 6 | public widgets = widgets; 7 | 8 | public styleSheets = [styles]; 9 | } 10 | -------------------------------------------------------------------------------- /packages/system/src/index.ts: -------------------------------------------------------------------------------- 1 | import './form.def.js'; 2 | 3 | export { JsfSystem } from './form.js'; 4 | export * as widgets from './widgets/index.js'; 5 | 6 | export type * from '@jsfe/types'; 7 | export type { Jsf } from '@jsfe/form'; 8 | -------------------------------------------------------------------------------- /packages/system/src/styles.scss: -------------------------------------------------------------------------------- 1 | // keep-sorted start 2 | // @import './widgets/callout.scss'; 3 | // @import './widgets/_all.scss'; 4 | @import './widgets/object.scss'; 5 | @import './widgets/submit.scss'; 6 | // keep-sorted end 7 | -------------------------------------------------------------------------------- /packages/system/src/styles.ts: -------------------------------------------------------------------------------- 1 | /* STUB */ 2 | 3 | import { css } from 'lit'; 4 | 5 | export const styles = css` 6 | /* STUB - Compiled SCSS goes here */ 7 | `; 8 | -------------------------------------------------------------------------------- /packages/system/src/widgets/_field.ts: -------------------------------------------------------------------------------- 1 | import { nothing, html, type TemplateResult } from 'lit'; 2 | 3 | import type { WidgetBaseParams } from '@jsfe/types'; 4 | 5 | export const field = ( 6 | options: WidgetBaseParams, 7 | children: TemplateResult<1>, 8 | constraints?: TemplateResult<1>, 9 | ) => html` 10 | 25 | `; 26 | -------------------------------------------------------------------------------- /packages/system/src/widgets/callout.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { classMap } from 'lit/directives/class-map.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | export const callout: Widgets['callout'] = (options) => { 7 | console.warn(options.message); 8 | return html` 9 | 19 | `; 20 | }; 21 | 22 | export const flag = (feature: string) => { 23 | const message = `\`${feature}\` is not supported yet, but you can preview it with \`experimental = { ${feature}: true, ... }\``; 24 | console.error(message); 25 | return html` 26 | 35 | `; 36 | }; 37 | -------------------------------------------------------------------------------- /packages/system/src/widgets/checkbox.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | export const checkbox: Widgets['checkbox'] = (options) => 6 | html` `; 32 | -------------------------------------------------------------------------------- /packages/system/src/widgets/color-picker.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | import { field } from './_field.js'; 6 | 7 | export const colorPicker: Widgets['colorPicker'] = (options) => html` 8 | ${field( 9 | options, 10 | html` { 18 | const newValue = (event.target as HTMLInputElement).value; 19 | options.valueChangedCallback?.(newValue); 20 | }} 21 | @keydown=${options.handleKeydown} 22 | />`, 23 | )} 24 | `; 25 | -------------------------------------------------------------------------------- /packages/system/src/widgets/date.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import { field } from './_field.js'; 5 | 6 | import type { Widgets } from '@jsfe/types'; 7 | 8 | export const date: Widgets['date'] = (options) => html` 9 | ${field( 10 | options, 11 | html` { 18 | const { valueAsDate: newValue } = event.target as HTMLInputElement; 19 | options.valueChangedCallback?.(newValue ?? undefined); 20 | }} 21 | />`, 22 | )} 23 | `; 24 | -------------------------------------------------------------------------------- /packages/system/src/widgets/index.ts: -------------------------------------------------------------------------------- 1 | // import './defs/range.def.js'; 2 | 3 | // keep-sorted start 4 | 5 | export { callout, flag } from './callout.js'; 6 | export { checkbox } from './checkbox.js'; 7 | export { colorPicker } from './color-picker.js'; 8 | export { date } from './date.js'; 9 | export { number } from './number.js'; 10 | export { object } from './object.js'; 11 | export { radioGroup } from './radio-group.js'; 12 | export { radioGroupBoolean } from './radio-group-boolean.js'; 13 | export { range } from './range.js'; 14 | export { select } from './select.js'; 15 | export { selectMultiple } from './select-multiple.js'; 16 | export { submit } from './submit.js'; 17 | export { text } from './text.js'; 18 | export { textarea } from './textarea.js'; 19 | // keep-sorted end 20 | 21 | // export { array } from '../../shoelace/src/widgets/array.js'; 22 | // export { date } from './widgets/date.js'; 23 | -------------------------------------------------------------------------------- /packages/system/src/widgets/number.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import { field } from './_field.js'; 7 | 8 | export const number: Widgets['number'] = (options) => html` 9 | ${field( 10 | options, 11 | html` { 18 | const newValue = (event.target as HTMLInputElement).valueAsNumber; 19 | console.log(newValue); 20 | options.valueChangedCallback?.(newValue); 21 | }} 22 | />`, 23 | )} 24 | `; 25 | -------------------------------------------------------------------------------- /packages/system/src/widgets/object.scss: -------------------------------------------------------------------------------- 1 | .theme-system.widget-object { 2 | select, 3 | input:not([type='radio'], [type='checkbox']), 4 | textarea { 5 | width: 100%; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/system/src/widgets/object.ts: -------------------------------------------------------------------------------- 1 | import type { Widgets } from '@jsfe/types'; 2 | import { nothing, html } from 'lit'; 3 | 4 | export const object: Widgets['object'] = (options) => html` 5 |
10 | ${options.label ? html`${options.label}` : nothing} 11 | 12 | ${options.helpText 13 | ? html`

${options.helpText}

` 14 | : nothing} 15 | 16 | ${options.children} 17 |
18 | `; 19 | -------------------------------------------------------------------------------- /packages/system/src/widgets/radio-group-boolean.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | // import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | export const radioGroupBoolean: Widgets['radioGroupBoolean'] = (options) => { 7 | function radio(i: boolean) { 8 | return html``; 26 | } 27 | 28 | return html` 29 |
30 | ${options.label ? html`${options.label}` : nothing} 31 | 32 | ${options.helpText 33 | ? html`

${options.helpText}

` 34 | : nothing} 35 | 36 | ${radio(false)} ${radio(true)} 37 |
38 | `; 39 | }; 40 | -------------------------------------------------------------------------------- /packages/system/src/widgets/radio-group.ts: -------------------------------------------------------------------------------- 1 | import { html, nothing } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import { field } from './_field.js'; 7 | 8 | export const radioGroup: Widgets['radioGroup'] = (options) => html` 9 |
10 | ${options.label ? html`${options.label}` : nothing} 11 | 12 | ${options.helpText 13 | ? html`

${options.helpText}

` 14 | : nothing} 15 | 16 | ${options.enum?.map( 17 | (enumVal) => 18 | html``, 34 | )} 35 |
36 | `; 37 | -------------------------------------------------------------------------------- /packages/system/src/widgets/range.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | import { field } from './_field.js'; 6 | 7 | export const range: Widgets['range'] = (options) => html` 8 | ${options.step} 9 | ${field( 10 | options, 11 | html` { 18 | const newValue = (event.target as HTMLInputElement).valueAsNumber; 19 | console.log(newValue); 20 | options.valueChangedCallback?.(newValue); 21 | }} 22 | />`, 23 | html`${options.value ?? '-'}`, 24 | )} 25 | `; 26 | -------------------------------------------------------------------------------- /packages/system/src/widgets/select-multiple.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | import { field } from './_field.js'; 6 | 7 | export const selectMultiple: Widgets['selectMultiple'] = (options) => html` 8 | ${field( 9 | options, 10 | html` `, 41 | typeof options.maxLength !== 'undefined' 42 | ? html`${options.value?.length} / ${options.maxLength}` 43 | : undefined, 44 | )} 45 | `; 46 | -------------------------------------------------------------------------------- /packages/system/src/widgets/select.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import { field } from './_field.js'; 7 | 8 | export const select: Widgets['select'] = (options) => html` 9 | ${field( 10 | options, 11 | html``, 31 | )} 32 | `; 33 | -------------------------------------------------------------------------------- /packages/system/src/widgets/submit.scss: -------------------------------------------------------------------------------- 1 | .theme-system.widget-submit { 2 | display: flex; 3 | justify-content: center; 4 | margin: 2rem; 5 | } 6 | -------------------------------------------------------------------------------- /packages/system/src/widgets/submit.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | 3 | import type { Widgets } from '@jsfe/types'; 4 | 5 | export const submit: Widgets['submit'] = (options) => html` 6 | 7 |
8 | 9 |
10 | `; 11 | -------------------------------------------------------------------------------- /packages/system/src/widgets/text.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import { field } from './_field.js'; 7 | 8 | export const text: Widgets['text'] = (options) => html` 9 | ${field( 10 | options, 11 | html` { 22 | const newValue = (event.target as HTMLInputElement).value; 23 | options.valueChangedCallback?.(newValue); 24 | }} 25 | @keydown=${options.handleKeydown} 26 | />`, 27 | typeof options.maxLength !== 'undefined' 28 | ? html`${options.value?.length} / ${options.maxLength}` 29 | : undefined, 30 | )} 31 | `; 32 | -------------------------------------------------------------------------------- /packages/system/src/widgets/textarea.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import { field } from './_field.js'; 7 | 8 | export const textarea: Widgets['textarea'] = (options) => html` 9 | ${field( 10 | options, 11 | html``, 24 | typeof options.maxLength !== 'undefined' 25 | ? html`${options.value?.length} / ${options.maxLength}` 26 | : undefined, 27 | // autosize 28 | )} 29 | `; 30 | -------------------------------------------------------------------------------- /packages/system/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "Bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "outDir": "dist/esm", 22 | "declaration": true, 23 | 24 | "removeComments": true, 25 | 26 | "noImplicitAny": true, 27 | 28 | "declarationMap": true 29 | }, 30 | "include": [ 31 | // 32 | "src/index.ts" 33 | ], 34 | "exclude": ["src/styles.ts"] 35 | } 36 | -------------------------------------------------------------------------------- /packages/types/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.4.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/types@0.3.1...@jsfe/types@0.4.0) (2024-06-06) 7 | 8 | ### Bug Fixes 9 | 10 | - add types field to package.json ([#8](https://github.com/json-schema-form-element/jsfe/issues/8)) ([e0ecf92](https://github.com/json-schema-form-element/jsfe/commit/e0ecf923475ca123ea9ee76d331d35d68c591102)) 11 | 12 | ### Features 13 | 14 | - submit button custom text ([e751828](https://github.com/json-schema-form-element/jsfe/commit/e751828b3bc07d21fc537b97e1b531a94a77fd70)), closes [#7](https://github.com/json-schema-form-element/jsfe/issues/7) 15 | 16 | ## [0.3.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/types@0.3.0...@jsfe/types@0.3.1) (2023-11-30) 17 | 18 | **Note:** Version bump only for package @jsfe/types 19 | 20 | # [0.3.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/types@0.2.0...@jsfe/types@0.3.0) (2023-11-30) 21 | 22 | ### Features 23 | 24 | - "disabled" and "read only" ui schema options ([01952e2](https://github.com/json-schema-form-element/jsfe/commit/01952e23355ffd5ce3cd13f8a8e5de4b05cc852c)) 25 | 26 | # [0.2.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/types@0.1.1...@jsfe/types@0.2.0) (2023-11-30) 27 | 28 | ### Features 29 | 30 | - ui schema title + desc., better date formats ([208e596](https://github.com/json-schema-form-element/jsfe/commit/208e59600d2a6811d0596572f1bc1ea4f240a945)) 31 | 32 | ## [0.1.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/types@0.1.0...@jsfe/types@0.1.1) (2023-10-12) 33 | 34 | ### Bug Fixes 35 | 36 | - better styles, material export ([9a33cbb](https://github.com/json-schema-form-element/jsfe/commit/9a33cbb29059ac8827647db6a7deda45d9cb3c09)) 37 | 38 | # 0.1.0 (2023-10-11) 39 | 40 | ### Bug Fixes 41 | 42 | - trigger ci release ([39824c4](https://github.com/json-schema-form-element/jsfe/commit/39824c4cab8ab325922b4f98fcc3c900d9904dd2)) 43 | 44 | ### Features 45 | 46 | - add base packages files ([d8e42bd](https://github.com/json-schema-form-element/jsfe/commit/d8e42bdcda5f8af5e2728e1556946d333e7f59b5)) 47 | - add components for all ui libraries packages ([7a6d3a5](https://github.com/json-schema-form-element/jsfe/commit/7a6d3a53f3939d00512c9f42925d1f9f1db246ff)) 48 | - extract widgets from core to packages ([fc247f6](https://github.com/json-schema-form-element/jsfe/commit/fc247f6475567deefde803ad5aedd35f3b9e70d7)) 49 | - re-organize to mono-repo ([3888c62](https://github.com/json-schema-form-element/jsfe/commit/3888c62a07b07aed2262c7e0c7b66919f30505ef)) 50 | - select multiple, typings fixes, refactor ([463aa85](https://github.com/json-schema-form-element/jsfe/commit/463aa85d7ba22480513bc485ab4ad849e39c5402)) 51 | -------------------------------------------------------------------------------- /packages/types/README.md: -------------------------------------------------------------------------------- 1 | # JSON Schema Form Element — ***Types*** edition 2 | 3 | ```sh 4 | npm install @jsfe/types 5 | ``` 6 | 7 | - Consult the [documentation](../../README.md). 8 | - Open the [playground](https://jsfe.js.org). 9 | - Try the [examples](https://github.com/json-schema-form-element/examples#readme). 10 | 11 | --- 12 | 13 | # `packages/types/src/index.ts`: 14 | 15 | ## Exports 16 | 17 | | Kind | Name | Declaration | Module | Package | 18 | | ---- | ------------- | ----------- | ------ | ----------------- | 19 | | `js` | `*` | \* | | ./ui-schema.js | 20 | | `js` | `*` | \* | | ./widgets.js | 21 | | `js` | `*` | \* | | ./form.js | 22 | | `js` | `JSONSchema7` | JSONSchema7 | | json-schema | 23 | | `js` | `FromSchema` | FromSchema | | json-schema-to-ts | 24 | 25 | -------------------------------------------------------------------------------- /packages/types/custom-elements.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "readme": "", 4 | "modules": [ 5 | { 6 | "kind": "javascript-module", 7 | "path": "packages/types/src/form.ts", 8 | "declarations": [], 9 | "exports": [] 10 | }, 11 | { 12 | "kind": "javascript-module", 13 | "path": "packages/types/src/index.ts", 14 | "declarations": [], 15 | "exports": [ 16 | { 17 | "kind": "js", 18 | "name": "*", 19 | "declaration": { 20 | "name": "*", 21 | "package": "./ui-schema.js" 22 | } 23 | }, 24 | { 25 | "kind": "js", 26 | "name": "*", 27 | "declaration": { 28 | "name": "*", 29 | "package": "./widgets.js" 30 | } 31 | }, 32 | { 33 | "kind": "js", 34 | "name": "*", 35 | "declaration": { 36 | "name": "*", 37 | "package": "./form.js" 38 | } 39 | }, 40 | { 41 | "kind": "js", 42 | "name": "JSONSchema7", 43 | "declaration": { 44 | "name": "JSONSchema7", 45 | "package": "json-schema" 46 | } 47 | }, 48 | { 49 | "kind": "js", 50 | "name": "FromSchema", 51 | "declaration": { 52 | "name": "FromSchema", 53 | "package": "json-schema-to-ts" 54 | } 55 | } 56 | ] 57 | }, 58 | { 59 | "kind": "javascript-module", 60 | "path": "packages/types/src/ui-schema.ts", 61 | "declarations": [], 62 | "exports": [] 63 | }, 64 | { 65 | "kind": "javascript-module", 66 | "path": "packages/types/src/widgets.ts", 67 | "declarations": [], 68 | "exports": [] 69 | } 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /packages/types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/types", 3 | "version": "0.4.0", 4 | "homepage": "https://jsfe.js.org", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/json-schema-form-element/jsfe", 8 | "directory": "packages/types" 9 | }, 10 | "type": "module", 11 | "types": "./dist/index.d.ts", 12 | "exports": { 13 | ".": "./dist/index.js" 14 | }, 15 | "files": [ 16 | "dist/*" 17 | ], 18 | "scripts": { 19 | "build": "tsc", 20 | "dev": "tsc --watch" 21 | }, 22 | "dependencies": { 23 | "@types/json-schema": "^7.0.15", 24 | "json-schema-to-ts": "^2.9.2" 25 | }, 26 | "peerDependencies": { 27 | "lit": "^3.1.0" 28 | }, 29 | "publishConfig": { 30 | "access": "public" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/types/src/form.ts: -------------------------------------------------------------------------------- 1 | // NOTE: Unused. Should rename to "Registry"? 2 | // export type Theme = 3 | // | 'system' 4 | // | 'shoelace' 5 | // | 'material' 6 | // | 'carbon' 7 | // | 'wired' 8 | // | 'custom'; 9 | 10 | export type Path = (string | number)[]; 11 | 12 | export interface FeatureFlags { 13 | allOf?: boolean; 14 | oneOf?: boolean; 15 | additionalItems?: boolean; 16 | additionalProperties?: boolean; 17 | } 18 | 19 | export type DataChangeCallback = ( 20 | newData: unknown, 21 | path: Path, 22 | value: unknown, 23 | schemaPath: Path, 24 | ) => void; 25 | 26 | export type OnFormSubmit = (newData: unknown, valid: boolean) => void; 27 | -------------------------------------------------------------------------------- /packages/types/src/index.ts: -------------------------------------------------------------------------------- 1 | export type * from './ui-schema.js'; 2 | export type * from './widgets.js'; 3 | export type * from './form.js'; 4 | 5 | export type { JSONSchema7 } from 'json-schema'; 6 | export type { FromSchema } from 'json-schema-to-ts'; 7 | -------------------------------------------------------------------------------- /packages/types/src/ui-schema.ts: -------------------------------------------------------------------------------- 1 | export type UiOptions = { 2 | 'ui:help'?: string; 3 | 'ui:placeholder'?: string; 4 | 5 | 'ui:title'?: string; 6 | 'ui:description'?: string; 7 | 8 | 'ui:disabled'?: boolean; 9 | 'ui:readonly'?: boolean; 10 | 11 | 'ui:widget'?: 12 | | 'radio' 13 | | 'button' 14 | | 'textarea' 15 | | 'color' 16 | | 'range' 17 | | 'password' 18 | | 'rating' 19 | | 'select' 20 | | 'switch'; 21 | }; 22 | 23 | type PropertyLevel = { 24 | [propertyLevelChildKey: string]: UiOptions | PropertyLevel; 25 | }; 26 | 27 | export type UiSchema = UiOptions | PropertyLevel; 28 | -------------------------------------------------------------------------------- /packages/types/src/widgets.ts: -------------------------------------------------------------------------------- 1 | import type { TemplateResult } from 'lit'; 2 | 3 | export interface WidgetBaseParams { 4 | id: string; 5 | required?: boolean; 6 | 7 | value?: V; 8 | label?: string; 9 | helpText?: string; 10 | placeholder?: string; 11 | 12 | valueChangedCallback?: (value?: V) => void; 13 | handleKeydown?: (event: KeyboardEvent) => void; 14 | } 15 | 16 | type Widget = ( 17 | args: WidgetBaseParams & T, 18 | ) => TemplateResult<1>; 19 | 20 | export interface Widgets { 21 | text?: Widget< 22 | { 23 | pattern?: string; 24 | maxLength?: number; 25 | minLength?: number; 26 | 27 | inputType: 'email' | 'password' | 'tel' | 'text' | 'url'; 28 | }, 29 | string 30 | >; 31 | 32 | textarea?: Widget< 33 | { 34 | maxLength?: number; 35 | minLength?: number; 36 | }, 37 | string 38 | >; 39 | 40 | colorPicker?: Widget< 41 | { 42 | // pattern?: string; 43 | }, 44 | string 45 | >; 46 | 47 | number?: Widget< 48 | { 49 | min?: number; 50 | max?: number; 51 | step?: number | 'any'; 52 | }, 53 | number 54 | >; 55 | 56 | date?: Widget< 57 | { 58 | type: 'date' | 'datetime-local' | 'time'; 59 | }, 60 | Date | string | undefined 61 | >; 62 | 63 | rating?: Widget< 64 | { 65 | min?: number; 66 | max?: number; 67 | step?: number | 'any'; 68 | }, 69 | number 70 | >; 71 | 72 | range?: Widget< 73 | { 74 | min?: number; 75 | max?: number; 76 | step?: number | 'any'; 77 | }, 78 | number 79 | >; 80 | 81 | checkbox?: Widget; 82 | 83 | switch?: Widget; 84 | 85 | radioGroupBoolean?: Widget< 86 | { 87 | trueLabel?: string; 88 | falseLabel?: string; 89 | }, 90 | boolean 91 | >; 92 | 93 | buttonGroupBoolean?: Widget< 94 | { 95 | trueLabel?: string; 96 | falseLabel?: string; 97 | }, 98 | boolean 99 | >; 100 | 101 | select?: Widget< 102 | { 103 | enum?: string[] | number[]; 104 | type: 'number' | 'integer' | 'string'; 105 | }, 106 | string | number 107 | >; 108 | 109 | object?: Widget< 110 | { 111 | children: TemplateResult<1> | TemplateResult<1>[]; 112 | level: number; 113 | }, 114 | unknown 115 | >; 116 | 117 | radioGroup?: Widget< 118 | { 119 | type: 'string' | 'number'; 120 | enum: string[] | number[]; 121 | }, 122 | string | number 123 | >; 124 | 125 | buttonGroup?: Widget< 126 | { 127 | type: 'string' | 'number'; 128 | enum: string[] | number[]; 129 | }, 130 | string | number 131 | >; 132 | 133 | checkboxGroup?: Widget< 134 | { 135 | // type: 'string' | 'number'; 136 | enum: (string | number)[]; 137 | 138 | // items: (string[]) 139 | }, 140 | // [string | number, boolean] 141 | (string | number)[] 142 | >; 143 | 144 | callout?: Widget<{ 145 | message: string; 146 | type?: 'tip' | 'warning' | 'danger'; 147 | }>; 148 | 149 | submit?: Widget<{ id?: string; label?: string }>; 150 | } 151 | -------------------------------------------------------------------------------- /packages/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "outDir": "dist", 22 | "declaration": true, 23 | // "emitDeclarationOnly": true, 24 | 25 | "removeComments": true, 26 | 27 | "noImplicitAny": true, 28 | 29 | "declarationMap": true 30 | }, 31 | "include": [ 32 | // 33 | "src/index.ts" 34 | ], 35 | "exclude": ["src/styles.ts"] 36 | } 37 | -------------------------------------------------------------------------------- /packages/wired/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.3.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/wired@0.2.2...@jsfe/wired@0.3.0) (2024-06-06) 7 | 8 | ### Bug Fixes 9 | 10 | - add types field to package.json ([#8](https://github.com/json-schema-form-element/jsfe/issues/8)) ([e0ecf92](https://github.com/json-schema-form-element/jsfe/commit/e0ecf923475ca123ea9ee76d331d35d68c591102)) 11 | 12 | ### Features 13 | 14 | - submit button custom text ([e751828](https://github.com/json-schema-form-element/jsfe/commit/e751828b3bc07d21fc537b97e1b531a94a77fd70)), closes [#7](https://github.com/json-schema-form-element/jsfe/issues/7) 15 | 16 | ### Reverts 17 | 18 | - "style: bulk reformat with new imports orders" ([1236078](https://github.com/json-schema-form-element/jsfe/commit/12360786626927f2c8a0273b245b906e5946b920)) 19 | 20 | ## [0.2.2](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/wired@0.2.1...@jsfe/wired@0.2.2) (2023-11-30) 21 | 22 | **Note:** Version bump only for package @jsfe/wired 23 | 24 | ## [0.2.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/wired@0.2.0...@jsfe/wired@0.2.1) (2023-11-30) 25 | 26 | **Note:** Version bump only for package @jsfe/wired 27 | 28 | # [0.2.0](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/wired@0.1.1...@jsfe/wired@0.2.0) (2023-11-30) 29 | 30 | ### Features 31 | 32 | - more system / wired widgets, clean others ([bfcac92](https://github.com/json-schema-form-element/jsfe/commit/bfcac9247ded39af312b2df99a21a6d94d37c965)) 33 | 34 | ## [0.1.1](https://github.com/json-schema-form-element/jsfe/compare/@jsfe/wired@0.1.0...@jsfe/wired@0.1.1) (2023-10-12) 35 | 36 | ### Bug Fixes 37 | 38 | - better styles, material export ([9a33cbb](https://github.com/json-schema-form-element/jsfe/commit/9a33cbb29059ac8827647db6a7deda45d9cb3c09)) 39 | 40 | # 0.1.0 (2023-10-11) 41 | 42 | ### Features 43 | 44 | - add base packages files ([d8e42bd](https://github.com/json-schema-form-element/jsfe/commit/d8e42bdcda5f8af5e2728e1556946d333e7f59b5)) 45 | - add components for all ui libraries packages ([7a6d3a5](https://github.com/json-schema-form-element/jsfe/commit/7a6d3a53f3939d00512c9f42925d1f9f1db246ff)) 46 | - re-organize to mono-repo ([3888c62](https://github.com/json-schema-form-element/jsfe/commit/3888c62a07b07aed2262c7e0c7b66919f30505ef)) 47 | - select multiple, typings fixes, refactor ([463aa85](https://github.com/json-schema-form-element/jsfe/commit/463aa85d7ba22480513bc485ab4ad849e39c5402)) 48 | -------------------------------------------------------------------------------- /packages/wired/README.md: -------------------------------------------------------------------------------- 1 | # JSON Schema Form Element — ***Wired*** edition 2 | 3 | ```sh 4 | npm install @jsfe/wired 5 | ``` 6 | 7 | - Consult the [documentation](../../README.md). 8 | - Open the [playground](https://jsfe.js.org). 9 | - Try the [examples](https://github.com/json-schema-form-element/examples#readme). 10 | 11 | --- 12 | 13 | # `packages/wired/src/form.def.ts`: 14 | 15 | ## Exports 16 | 17 | | Kind | Name | Declaration | Module | Package | 18 | | --------------------------- | ----------- | ----------- | --------------------------- | ------- | 19 | | `custom-element-definition` | `jsf-wired` | JsfWired | /packages/wired/src/form.js | | 20 | 21 | # `packages/wired/src/form.ts`: 22 | 23 | ## class: `JsfWired`, `jsf-wired` 24 | 25 | ### Superclass 26 | 27 | | Name | Module | Package | 28 | | ----- | ------ | ---------- | 29 | | `Jsf` | | @jsfe/form | 30 | 31 | ### Fields 32 | 33 | | Name | Privacy | Type | Default | Description | Inherited From | 34 | | ------------- | ------- | ------- | ---------- | ----------- | -------------- | 35 | | `widgets` | public | | `widgets` | | | 36 | | `styleSheets` | public | `array` | `[styles]` | | | 37 | 38 |
39 | 40 | ## Exports 41 | 42 | | Kind | Name | Declaration | Module | Package | 43 | | ---- | ---------- | ----------- | -------------------------- | ------- | 44 | | `js` | `JsfWired` | JsfWired | packages/wired/src/form.ts | | 45 | 46 | # `packages/wired/src/index.ts`: 47 | 48 | ## Exports 49 | 50 | | Kind | Name | Declaration | Module | Package | 51 | | ---- | ---------- | ----------- | --------- | ------------------ | 52 | | `js` | `JsfWired` | JsfWired | ./form.js | | 53 | | `js` | `*` | \* | | ./widgets/index.js | 54 | | `js` | `*` | \* | | @jsfe/types | 55 | | `js` | `Jsf` | Jsf | | @jsfe/form | 56 | 57 | # `packages/wired/src/styles.ts`: 58 | 59 | ## Variables 60 | 61 | | Name | Description | Type | 62 | | -------- | ----------- | ---- | 63 | | `styles` | | | 64 | 65 |
66 | 67 | ## Exports 68 | 69 | | Kind | Name | Declaration | Module | Package | 70 | | ---- | -------- | ----------- | ---------------------------- | ------- | 71 | | `js` | `styles` | styles | packages/wired/src/styles.ts | | 72 | 73 | -------------------------------------------------------------------------------- /packages/wired/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/wired", 3 | "version": "0.3.0", 4 | "description": "Wired v3 forms, auto-generated by JSON schemas.", 5 | "keywords": [ 6 | "json-schema", 7 | "wired", 8 | "forms", 9 | "generation", 10 | "declarative", 11 | "openapi", 12 | "mongodb", 13 | "page-builder" 14 | ], 15 | "homepage": "https://jsfe.js.org", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/json-schema-form-element/jsfe", 19 | "directory": "packages/wired" 20 | }, 21 | "license": "ISC", 22 | "author": { 23 | "name": "Julian Cataldo", 24 | "email": "contact@juliancataldo.com", 25 | "url": "https://www.juliancataldo.com" 26 | }, 27 | "type": "module", 28 | "types": "./dist/esm/index.d.ts", 29 | "exports": { 30 | ".": "./dist/esm/index.js", 31 | "./form": "./dist/esm/jsf-wired.js", 32 | "./scss": "./src/styles.scss", 33 | "./css": "./dist/esm/styles.css", 34 | "./jss": "./dist/esm/styles.js", 35 | "./min": "./dist/esm-min" 36 | }, 37 | "files": [ 38 | "./dist/esm", 39 | "./dist/esm-min", 40 | "./src/**/*.scss", 41 | "./vscode.html-custom-data.json", 42 | "./vscode.css-custom-data.json", 43 | "./custom-elements.json" 44 | ], 45 | "scripts": { 46 | "build": "pnpm clean ; pnpm ts:build ; pnpm css:build ; pnpm css:to-js", 47 | "clean": "rm -rf ./dist", 48 | "css:build": "pnpm sass --no-source-map src/styles.scss:dist/esm/styles.css", 49 | "css:dev": "pnpm sass --watch src/styles.scss:dist/esm/styles.css & pnpm css:to-js:dev", 50 | "css:to-js": "node ../../scripts/css-to-js.js dist/esm/styles.css", 51 | "css:to-js:dev": "nodemon dist/esm/styles.css -x 'pnpm css:to-js'", 52 | "dev": "pnpm ts:dev & pnpm css:dev & (sleep 3 && pnpm css:to-js:dev)", 53 | "ts:build": "pnpm tsc", 54 | "ts:dev": "pnpm tsc --watch" 55 | }, 56 | "dependencies": { 57 | "@jsfe/form": "workspace:*", 58 | "@jsfe/types": "workspace:*" 59 | }, 60 | "devDependencies": { 61 | "sass": "^1.69.5", 62 | "typescript": "^5.3.2" 63 | }, 64 | "peerDependencies": { 65 | "lit": "^3.1.0", 66 | "wired-elements": "3.0.0-rc.6" 67 | }, 68 | "publishConfig": { 69 | "access": "public" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/wired/src/form.def.ts: -------------------------------------------------------------------------------- 1 | import { JsfWired } from './form.js'; 2 | 3 | customElements.define('jsf-wired', JsfWired); 4 | 5 | declare global { 6 | interface HTMLElementTagNameMap { 7 | 'jsf-wired': JsfWired; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/wired/src/form.ts: -------------------------------------------------------------------------------- 1 | import { Jsf } from '@jsfe/form'; 2 | import * as widgets from './widgets/index.js'; 3 | import { styles } from './styles.js'; 4 | 5 | export class JsfWired extends Jsf { 6 | public widgets = widgets; 7 | 8 | public styleSheets = [styles]; 9 | } 10 | -------------------------------------------------------------------------------- /packages/wired/src/index.ts: -------------------------------------------------------------------------------- 1 | import './form.def.js'; 2 | 3 | export { JsfWired } from './form.js'; 4 | export * as widgets from './widgets/index.js'; 5 | 6 | export type * from '@jsfe/types'; 7 | export type { Jsf } from '@jsfe/form'; 8 | -------------------------------------------------------------------------------- /packages/wired/src/styles.scss: -------------------------------------------------------------------------------- 1 | // keep-sorted start 2 | @import './widgets/_all.scss'; 3 | @import './widgets/_field.scss'; 4 | @import './widgets/object.scss'; 5 | @import './widgets/range.scss'; 6 | @import './widgets/submit.scss'; 7 | @import './widgets/text.scss'; 8 | @import './widgets/textarea.scss'; 9 | // keep-sorted end 10 | -------------------------------------------------------------------------------- /packages/wired/src/styles.ts: -------------------------------------------------------------------------------- 1 | /* STUB */ 2 | 3 | import { css } from 'lit'; 4 | 5 | export const styles = css` 6 | /* STUB - Compiled SCSS goes here */ 7 | `; 8 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/_all.scss: -------------------------------------------------------------------------------- 1 | .theme-wired { 2 | /* Custom vars (not part of the design system) */ 3 | --wired-font-cursive: 'Gloria Hallelujah', cursive; 4 | 5 | /* ------------------------------------------------------------------------ */ 6 | 7 | box-sizing: border-box; 8 | font: 16px var(--wired-font-cursive); 9 | // font-weight: ///; 10 | // line-height: ///; 11 | // color: ///; 12 | -moz-osx-font-smoothing: grayscale; 13 | -webkit-font-smoothing: antialiased; 14 | 15 | *, 16 | *::before, 17 | *::after { 18 | box-sizing: inherit; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/_field.scss: -------------------------------------------------------------------------------- 1 | .theme-wired.widget-field { 2 | // padding: 1rem; 3 | 4 | // TODO: 5 | // &:has(input:focus) { 6 | // color: var(--wired-accent-color); 7 | // } 8 | } 9 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/_field.ts: -------------------------------------------------------------------------------- 1 | import { nothing, html, type TemplateResult } from 'lit'; 2 | 3 | import type { WidgetBaseParams } from '@jsfe/types'; 4 | 5 | export const field = ( 6 | options: WidgetBaseParams, 7 | children: TemplateResult<1>, 8 | constraints?: TemplateResult<1>, 9 | ) => html` 10 | 25 | `; 26 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/index.ts: -------------------------------------------------------------------------------- 1 | // keep-sorted start 2 | export { number } from './number.js'; 3 | export { object } from './object.js'; 4 | export { range } from './range.js'; 5 | export { submit } from './submit.js'; 6 | export { text } from './text.js'; 7 | // export { switchh as switch } from './.dev/switch.js'; 8 | export { textarea } from './textarea.js'; 9 | // keep-sorted end 10 | 11 | // export { checkbox } from './checkbox.js'; 12 | // export { enumeration } from './enumeration.js'; 13 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/number.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import { field } from './_field.js'; 7 | 8 | import 'wired-elements/lib/wired-input.js'; 9 | import type { WiredInput } from 'wired-elements/lib/wired-input.js'; 10 | 11 | export const number: Widgets['number'] = (options) => html` 12 | ${field( 13 | options, 14 | html` { 25 | const { value: newValue } = event.target as WiredInput; 26 | options.valueChangedCallback?.(newValue); 27 | }} 28 | @keydown=${options.handleKeydown} 29 | > 30 | `, 31 | )} 32 | `; 33 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/object.scss: -------------------------------------------------------------------------------- 1 | .theme-wired.widget-object { 2 | padding: 1.5rem 0; 3 | margin: 0; 4 | margin-top: 0rem; 5 | border: none; 6 | // width: 100%; 7 | // position: relative; 8 | // display: flex; 9 | // flex-direction: column; 10 | // gap: 6rem; 11 | wired-card { 12 | width: 100%; 13 | } 14 | 15 | .widget-object__field { 16 | display: flex; 17 | flex-direction: column; 18 | gap: 1rem; 19 | padding: 1rem 1.5rem 1rem 0.5rem; 20 | // width: 100%; 21 | // flex-grow: 1; 22 | } 23 | 24 | legend { 25 | font-size: 1.3em; 26 | font-weight: 700; 27 | text-align: center; 28 | } 29 | 30 | wired-select, 31 | wired-slider, 32 | wired-textarea { 33 | width: 100%; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/object.ts: -------------------------------------------------------------------------------- 1 | import type { Widgets } from '@jsfe/types'; 2 | import { nothing, html } from 'lit'; 3 | 4 | import 'wired-elements/lib/wired-card.js'; 5 | 6 | export const object: Widgets['object'] = (options) => html` 7 |
12 | ${options.label ? html`${options.label}` : nothing} 13 | 14 | ${options.helpText 15 | ? html`

${options.helpText}

` 16 | : nothing} 17 | 18 |
19 | 20 | ${options.children} 21 |
22 |
23 |
24 | `; 25 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/range.scss: -------------------------------------------------------------------------------- 1 | wired-slider { 2 | --wired-slider-bar-color: currentColor; 3 | } 4 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/range.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import 'wired-elements/lib/wired-slider.js'; 7 | 8 | import type { WiredSlider } from 'wired-elements/lib/wired-slider.js'; 9 | 10 | export const range: Widgets['number'] = (options) => html` 11 | 12 | 13 |
${options.helpText}
14 | { 23 | const newValue = (event.target as WiredSlider).value; 24 | console.log(newValue); 25 | options.valueChangedCallback?.(newValue); 26 | console.log(newValue); 27 | }} 28 | > 29 | 30 | ${options.value ?? '-'} 31 | `; 32 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/submit.scss: -------------------------------------------------------------------------------- 1 | .theme-wired.widget-submit { 2 | display: flex; 3 | justify-content: center; 4 | margin: 2rem 0; 5 | 6 | wired-button { 7 | color: var(--wired-accent-color); 8 | 9 | &:hover { 10 | color: initial; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/submit.ts: -------------------------------------------------------------------------------- 1 | import 'wired-elements/lib/wired-button.js'; 2 | 3 | import { html } from 'lit'; 4 | 5 | import type { Widgets } from '@jsfe/types'; 6 | 7 | export const submit: Widgets['submit'] = (options) => html` 8 | 9 |
10 | 14 | // FIXME: !!! 15 | event.target?.dispatchEvent(new Event('submit', { bubbles: true }))} 16 | >${options.label ?? 'Submit'} 18 |
19 | `; 20 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/text.scss: -------------------------------------------------------------------------------- 1 | .theme-wired.widget-field { 2 | // margin: 1rem 0; 3 | 4 | wired-input { 5 | width: 100%; 6 | font-family: inherit; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/text.ts: -------------------------------------------------------------------------------- 1 | import { html } from 'lit'; 2 | import { ifDefined } from 'lit/directives/if-defined.js'; 3 | 4 | import type { Widgets } from '@jsfe/types'; 5 | 6 | import { field } from './_field.js'; 7 | 8 | import 'wired-elements/lib/wired-input.js'; 9 | import type { WiredInput } from 'wired-elements/lib/wired-input.js'; 10 | 11 | export const text: Widgets['text'] = (options) => html` 12 | ${field( 13 | options, 14 | html` { 26 | const { value: newValue } = event.target as WiredInput; 27 | options.valueChangedCallback?.(newValue); 28 | }} 29 | @keydown=${options.handleKeydown} 30 | > 31 | `, 32 | typeof options.maxLength !== 'undefined' 33 | ? html`${options.value?.length} / ${options.maxLength}` 34 | : undefined, 35 | )} 36 | `; 37 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/textarea.scss: -------------------------------------------------------------------------------- 1 | .theme-wired.widget-field { 2 | wired-textarea { 3 | width: 100%; 4 | font-family: inherit; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/wired/src/widgets/textarea.ts: -------------------------------------------------------------------------------- 1 | import { field } from './_field.js'; 2 | import { html } from 'lit'; 3 | 4 | import 'wired-elements/lib/wired-textarea.js'; 5 | import type { WiredTextarea } from 'wired-elements/lib/wired-textarea.js'; 6 | 7 | import type { Widgets } from '@jsfe/types'; 8 | import { ifDefined } from 'lit/directives/if-defined.js'; 9 | 10 | export const textarea: Widgets['text'] = (options) => html` 11 | ${field( 12 | options, 13 | html` { 25 | const { value: newValue } = event.target as WiredTextarea; 26 | options.valueChangedCallback?.(newValue); 27 | }} 28 | @keydown=${options.handleKeydown} 29 | > 30 | `, 31 | typeof options.maxLength !== 'undefined' 32 | ? html`${options.value?.length} / ${options.maxLength}` 33 | : undefined, 34 | )} 35 | `; 36 | -------------------------------------------------------------------------------- /packages/wired/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "Bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "outDir": "dist/esm", 22 | "declaration": true, 23 | 24 | "removeComments": true, 25 | 26 | "noImplicitAny": true, 27 | 28 | "declarationMap": true 29 | }, 30 | "include": [ 31 | // 32 | "src/index.ts" 33 | ], 34 | "exclude": ["src/styles.ts"] 35 | } 36 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/form' 3 | - 'packages/types' 4 | - 'packages/system' 5 | - 'packages/shoelace' 6 | - 'packages/spectrum' 7 | - 'packages/carbon' 8 | - 'packages/wired' 9 | - 'packages/material' 10 | # - 'packages/*' 11 | -------------------------------------------------------------------------------- /scripts/cem-to-md.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import { customElementsManifestToMarkdown } from '@custom-elements-manifest/to-markdown'; 3 | 4 | const packageDir = process.argv[2]; 5 | 6 | const manifest = JSON.parse( 7 | fs.readFileSync(`${packageDir}/custom-elements.json`, 'utf-8'), 8 | ); 9 | const markdown = customElementsManifestToMarkdown(manifest); 10 | 11 | const theme = packageDir.split('/').at(-1); 12 | const name = theme.at(0).toUpperCase() + theme.substring(1); 13 | const edition = theme === 'form' ? 'Barebone' : name; 14 | 15 | fs.writeFileSync( 16 | `${packageDir}/README.md`, 17 | `# JSON Schema Form Element — ***${edition}*** edition 18 | 19 | \`\`\`sh 20 | npm install @jsfe/${theme} 21 | \`\`\` 22 | 23 | - Consult the [documentation](../../README.md). 24 | - Open the [playground](https://jsfe.js.org). 25 | - Try the [examples](https://github.com/json-schema-form-element/examples#readme). 26 | 27 | --- 28 | 29 | ${markdown} 30 | `, 31 | ); 32 | -------------------------------------------------------------------------------- /scripts/css-to-js.js: -------------------------------------------------------------------------------- 1 | import * as fs from 'node:fs'; 2 | 3 | for (let i = 2; i < process.argv.length; i += 1) { 4 | try { 5 | const filePath = process.argv[i]; 6 | 7 | const content = fs.readFileSync(filePath); 8 | fs.writeFileSync( 9 | `${filePath.replace('.css', '')}.js`, 10 | `import { css } from 'lit'; 11 | 12 | export const styles = css\`${content.toString('utf8')}\`; 13 | `, 14 | ); 15 | // fs.writeFileSync( 16 | // `${filePath.replace('.css', '')}.d.ts`, 17 | // 'export const styles: CSSResult;', 18 | // ); 19 | } catch (error) { 20 | console.error(error); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /scripts/manifests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | pnpm cem analyze --globs packages/form/src/*.ts --litelement --outdir packages/form 4 | 5 | for dir in ./packages/*; do 6 | echo "$dir" 7 | pnpm cem analyze --globs $dir/src/*.ts --litelement --outdir $dir 8 | node scripts/cem-to-md.js $dir 9 | done 10 | -------------------------------------------------------------------------------- /tests/__fixtures__/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/__fixtures__/prepare.ts: -------------------------------------------------------------------------------- 1 | import { type Page } from 'playwright'; 2 | 3 | import type { JSONSchema7, FromSchema } from '../../lib/index.js'; 4 | 5 | export async function prepare( 6 | page: Page, 7 | initialData: any, 8 | schema: JSONSchema7, 9 | ) { 10 | await page.goto('/'); 11 | 12 | await page.evaluate( 13 | ({ initialData, schema }) => { 14 | const dump = document.querySelector('#dump')!; 15 | const jsfe = document.createElement('json-schema-form'); 16 | 17 | document.body.appendChild(jsfe); 18 | 19 | jsfe.schema = schema; 20 | 21 | jsfe.data = initialData; 22 | 23 | jsfe.dataChangeCallback = (newData) => { 24 | console.log(newData); 25 | console.log({ newData: JSON.stringify(newData) }); 26 | dump.innerText = JSON.stringify(newData); 27 | }; 28 | 29 | jsfe.onFormSubmit = (newData, valid) => { 30 | dump.innerText = JSON.stringify(newData); 31 | console.log({ newData: JSON.stringify(newData), valid }); 32 | }; 33 | }, 34 | { initialData, schema }, 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /tests/numbers.test.ts: -------------------------------------------------------------------------------- 1 | import test, { expect } from 'playwright/test'; 2 | import type { JSONSchema7, FromSchema } from '../lib/index.js'; 3 | import { prepare } from './__fixtures__/prepare.js'; 4 | 5 | const schema = { 6 | type: 'object', 7 | title: 'Numbers', 8 | properties: { 9 | foo: { 10 | default: 4, 11 | type: 'number', 12 | }, 13 | bar: { 14 | default: 2, 15 | type: 'number', 16 | maximum: 12, 17 | }, 18 | }, 19 | } as const satisfies JSONSchema7; 20 | type Data = FromSchema; 21 | 22 | const initialData: Data = { foo: 123 }; 23 | 24 | test('Numbers', async ({ page }) => { 25 | await prepare(page, initialData, schema); 26 | 27 | expect(page.getByLabel('foo')).toBeVisible(); 28 | expect(page.getByLabel('foo')).toHaveValue('123'); 29 | expect(page.getByLabel('bar')).toBeVisible(); 30 | expect(page.getByLabel('bar')).toHaveValue('2'); 31 | 32 | await page.getByLabel('bar').fill('10'); 33 | expect(page.getByLabel('bar')).toHaveValue('10'); 34 | await page.getByLabel('foo').fill('14'); 35 | expect(page.getByLabel('foo')).toHaveValue('14'); 36 | await page.getByLabel('bar').fill('223'); 37 | 38 | expect(page.locator('#dump')).toHaveValue( 39 | JSON.stringify({ foo: 14, bar: 223 }), 40 | ); 41 | 42 | await page.getByRole('button', { name: 'Submit' }).click(); 43 | 44 | expect(page.locator('#bar[data-user-invalid]')).toBeVisible(); 45 | 46 | expect(page.locator('#dump')).toHaveValue( 47 | JSON.stringify({ foo: 14, bar: 223 }), 48 | ); 49 | }); 50 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsfe/tests", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "private": true, 6 | "scripts": { 7 | "test": "vite dev & playwright test --ui" 8 | }, 9 | "devDependencies": { 10 | "@playwright/test": "^1.38.1", 11 | "playwright": "^1.38.1", 12 | "vite": "^4.4.9" 13 | }, 14 | "dependencies": {} 15 | } 16 | -------------------------------------------------------------------------------- /tests/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | export default defineConfig({ 4 | // Look for test files in the "tests" directory, relative to this configuration file. 5 | testDir: 'tests', 6 | 7 | // Run all tests in parallel. 8 | fullyParallel: true, 9 | 10 | // Fail the build on CI if you accidentally left test.only in the source code. 11 | forbidOnly: !!process.env.CI, 12 | 13 | // Retry on CI only. 14 | retries: process.env.CI ? 2 : 0, 15 | 16 | // Opt out of parallel tests on CI. 17 | workers: process.env.CI ? 1 : undefined, 18 | 19 | // Reporter to use 20 | // reporter: 'html', 21 | 22 | use: { 23 | // Base URL to use in actions like `await page.goto('/')`. 24 | baseURL: 'http://localhost:9898', 25 | 26 | // Collect trace when retrying the failed test. 27 | trace: 'on-first-retry', 28 | }, 29 | // Configure projects for major browsers. 30 | projects: [ 31 | { 32 | name: 'chromium', 33 | use: { ...devices['Desktop Chrome'] }, 34 | }, 35 | ], 36 | // Run your local dev server before starting the tests. 37 | // webServer: { 38 | // command: 'pnpm vite dev', 39 | // url: 'http://localhost:9898', 40 | // reuseExistingServer: !process.env.CI, 41 | // }, 42 | }); 43 | -------------------------------------------------------------------------------- /tests/vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig({ 4 | root: 'tests/__fixtures__/', 5 | server: { port: 9898 }, 6 | }); 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "Bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true, 20 | 21 | "removeComments": true, 22 | 23 | "noImplicitAny": true, 24 | 25 | "noEmit": true 26 | }, 27 | "include": [ 28 | // 29 | "scripts/**/*.ts", 30 | // "scripts/scaffold/plopfile.js", 31 | "scripts/cem-to-md.js", 32 | "scripts/css-to-js.js" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "pipeline": { 4 | "build": { 5 | // A package's `build` script depends on that package's 6 | // dependencies and devDependencies 7 | // `build` tasks being completed first 8 | // (the `^` symbol signifies `upstream`). 9 | "dependsOn": ["^build"], 10 | // note: output globs are relative to each package's `package.json` 11 | // (and not the monorepo root) 12 | // "inputs": ["**/*.ts", "!**/packages/src/*/styles.ts"], 13 | "outputs": ["dist/**"] 14 | // "outputs": [".next/**", "!.next/cache/**"] 15 | 16 | // "cache": false 17 | }, 18 | "clean": {}, 19 | // "deploy": { 20 | // // A package's `deploy` script depends on the `build`, 21 | // // `test`, and `lint` scripts of the same package 22 | // // being completed. It also has no filesystem outputs. 23 | // "dependsOn": ["build", "test", "lint"] 24 | // }, 25 | // "test": { 26 | // // A package's `test` script depends on that package's 27 | // // own `build` script being completed first. 28 | // "dependsOn": ["build"], 29 | // // A package's `test` script should only be rerun when 30 | // // either a `.tsx` or `.ts` file has changed in `src` or `test` folders. 31 | // "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"] 32 | // }, 33 | // // A package's `lint` script has no dependencies and 34 | // // can be run whenever. It also has no filesystem outputs. 35 | // "lint": {}, 36 | "dev": { 37 | "cache": false, 38 | "persistent": true 39 | } 40 | } 41 | } 42 | --------------------------------------------------------------------------------