├── .changeset
├── README.md
├── beige-grapes-knock.md
├── chilled-feet-hide.md
├── config.json
├── dry-shoes-cover.md
├── dull-baboons-suffer.md
├── few-clouds-care.md
├── friendly-rings-repair.md
├── hungry-guests-boil.md
├── ninety-kiwis-kiss.md
├── quiet-fireants-join.md
├── seven-monkeys-beam.md
├── six-islands-thank.md
├── wild-icons-prove.md
└── wise-pumas-eat.md
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .github
├── pr-commenter.yml
└── workflows
│ ├── build.yml
│ ├── netlify.yml
│ └── release.yml
├── .gitignore
├── .gitmodules
├── .husky
└── pre-commit
├── .nvmrc
├── LICENSE
├── README.md
├── codecov.yml
├── demos
├── examples
│ ├── CHANGELOG.md
│ ├── DescriptionTooltip.ts
│ ├── ErrorSummary
│ │ └── index.ts
│ ├── InlineNestedShapes
│ │ └── index.ts
│ ├── LanguageMultiSelect
│ │ └── index.ts
│ ├── NestedShapesIndividually
│ │ ├── components.ts
│ │ └── renderer.ts
│ ├── StarRating
│ │ ├── index.ts
│ │ └── star-rating.ts
│ ├── XoneRenderer
│ │ └── index.ts
│ └── package.json
├── lit-html
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── custom-elements.json
│ ├── index.html
│ ├── package.json
│ ├── src
│ │ ├── configure.ts
│ │ ├── env.ts
│ │ ├── menu.ts
│ │ ├── menu
│ │ │ ├── formMenu.ts
│ │ │ ├── resource.ts
│ │ │ └── shape.ts
│ │ ├── shaperone-playground-lit.ts
│ │ └── state
│ │ │ ├── config.ts
│ │ │ ├── models
│ │ │ ├── components.ts
│ │ │ ├── index.ts
│ │ │ ├── playground.ts
│ │ │ ├── renderer.ts
│ │ │ ├── resource.ts
│ │ │ └── shape.ts
│ │ │ └── store.ts
│ └── vite.config.js
└── storybook
│ ├── .storybook
│ ├── main.ts
│ ├── preview-head.html
│ └── preview.tsx
│ ├── data
│ └── simple
│ │ └── john-doe.ttl
│ ├── global.d.ts
│ ├── lib
│ ├── env.ts
│ └── shaperone-demo.ts
│ ├── package.json
│ ├── shapes
│ └── simple
│ │ ├── datatypes.ttl
│ │ ├── first-last.ttl
│ │ └── unrestricted.ttl
│ ├── stories
│ ├── Getting Started.mdx
│ ├── Shoelace.stories.ts
│ ├── Simple.stories.ts
│ └── render.ts
│ └── vite.config.js
├── dist
├── .nojekyll
├── _coverpage.md
├── _media
│ ├── GitHub-Mark-32px.png
│ ├── anatomy.png
│ ├── configuration.png
│ ├── logo.png
│ ├── multi-select.png
│ ├── prism.css
│ ├── stack.png
│ └── star-rating.png
├── _sidebar.md
├── advanced.md
├── anatomy.md
├── components
│ └── implement.md
├── configuration.md
├── core.md
├── editors.md
├── editors
│ ├── dash.md
│ ├── matchers.md
│ └── metadata.md
├── extensions
│ ├── hydra.md
│ └── plugins.md
├── getting-started.md
├── index.html
├── overview.md
├── plugins
│ └── variables.js
├── renderer
│ ├── core.md
│ └── web-components.md
├── validation.md
└── variables.json
├── package-lock.json
├── package.json
├── packages
├── core-tests
│ ├── CHANGELOG.md
│ ├── DashEditors.test.ts
│ ├── components.test.ts
│ ├── lib
│ │ ├── components.test.ts
│ │ └── property.test.ts
│ ├── models
│ │ ├── components
│ │ │ └── reducers.test.ts
│ │ ├── editors
│ │ │ ├── effects
│ │ │ │ └── loadDash.test.ts
│ │ │ ├── lib
│ │ │ │ └── match.test.ts
│ │ │ └── reducers
│ │ │ │ ├── addMatchers.test.ts
│ │ │ │ ├── addMetadata.test.ts
│ │ │ │ └── decorate.test.ts
│ │ ├── forms
│ │ │ ├── effects
│ │ │ │ ├── addObject.test.ts
│ │ │ │ ├── lib
│ │ │ │ │ └── syncProperties.test.ts
│ │ │ │ ├── pushFocusNode.test.ts
│ │ │ │ ├── removeObject.test.ts
│ │ │ │ ├── replaceObjects.test.ts
│ │ │ │ ├── resources
│ │ │ │ │ └── setRoot.test.ts
│ │ │ │ ├── selectShape.test.ts
│ │ │ │ ├── setObjectValue.test.ts
│ │ │ │ ├── setPropertyObjects.test.ts
│ │ │ │ ├── shapes
│ │ │ │ │ └── setGraph.test.ts
│ │ │ │ ├── updateObject.test.ts
│ │ │ │ └── validate.test.ts
│ │ │ ├── lib
│ │ │ │ └── stateBuilder.test.ts
│ │ │ └── reducers
│ │ │ │ ├── addFormField.test.ts
│ │ │ │ ├── editors.test.ts
│ │ │ │ ├── multiEditors.test.ts
│ │ │ │ ├── popFocusNode.test.ts
│ │ │ │ ├── properties.test.ts
│ │ │ │ ├── pushFocusNode.test.ts
│ │ │ │ ├── removeObject.test.ts
│ │ │ │ ├── replaceFocusNodes.test.ts
│ │ │ │ ├── selectEditor.test.ts
│ │ │ │ ├── selectGroup.test.ts
│ │ │ │ ├── selectShape.test.ts
│ │ │ │ ├── truncateFocusNodes.test.ts
│ │ │ │ ├── updateObject.test.ts
│ │ │ │ └── validationReport.test.ts
│ │ ├── resources
│ │ │ ├── effects
│ │ │ │ └── forms
│ │ │ │ │ ├── addFormField.test.ts
│ │ │ │ │ ├── clearValue.test.ts
│ │ │ │ │ ├── createFocusNodeState.test.ts
│ │ │ │ │ ├── removeObject.test.ts
│ │ │ │ │ ├── setObjectValue.test.ts
│ │ │ │ │ └── setPropertyObjects.test.ts
│ │ │ ├── lib
│ │ │ │ └── objectValue.test.ts
│ │ │ └── reducers
│ │ │ │ └── setRoot.test.ts
│ │ ├── shapes
│ │ │ ├── lib
│ │ │ │ └── index.test.ts
│ │ │ └── reducers
│ │ │ │ └── setGraph.test.ts
│ │ └── validation
│ │ │ └── reducers
│ │ │ └── setValidator.test.ts
│ ├── package.json
│ └── test-setup.ts
├── core
│ ├── CHANGELOG.md
│ ├── DashEditors.ts
│ ├── components.ts
│ ├── env.ts
│ ├── esbuild.config.js
│ ├── index.ts
│ ├── lib
│ │ ├── RecursivePartial.ts
│ │ ├── components.ts
│ │ ├── components
│ │ │ ├── autoComplete.ts
│ │ │ ├── base
│ │ │ │ └── instancesSelect.ts
│ │ │ ├── blankNode.ts
│ │ │ ├── booleanSelect.ts
│ │ │ ├── datePicker.ts
│ │ │ ├── dateTimePicker.ts
│ │ │ ├── details.ts
│ │ │ ├── enumSelect.ts
│ │ │ ├── instancesSelect.ts
│ │ │ ├── richText.ts
│ │ │ ├── textArea.ts
│ │ │ ├── textAreaWithLang.ts
│ │ │ ├── textField.ts
│ │ │ ├── textFieldWithLang.ts
│ │ │ └── uri.ts
│ │ ├── datatypes.ts
│ │ ├── env
│ │ │ ├── ConstantsFactory.ts
│ │ │ └── NamespaceFactory.ts
│ │ ├── graph.ts
│ │ ├── mixins.ts
│ │ ├── order.ts
│ │ └── property.ts
│ ├── metadata.ts
│ ├── models
│ │ ├── components
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ └── decorate.ts
│ │ │ └── reducers.ts
│ │ ├── editors
│ │ │ ├── effects
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ └── match.ts
│ │ │ └── reducers
│ │ │ │ ├── addMatchers.ts
│ │ │ │ ├── addMetadata.ts
│ │ │ │ └── decorate.ts
│ │ ├── forms
│ │ │ ├── effects
│ │ │ │ ├── addObject.ts
│ │ │ │ ├── components
│ │ │ │ │ └── index.ts
│ │ │ │ ├── createFocusNodeState.ts
│ │ │ │ ├── editors
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── lib
│ │ │ │ │ └── syncProperties.ts
│ │ │ │ ├── notify.ts
│ │ │ │ ├── pushFocusNode.ts
│ │ │ │ ├── removeObject.ts
│ │ │ │ ├── replaceObjects.ts
│ │ │ │ ├── resources
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── setRoot.ts
│ │ │ │ ├── selectShape.ts
│ │ │ │ ├── setObjectValue.ts
│ │ │ │ ├── setPropertyObjects.ts
│ │ │ │ ├── shapes
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── setGraph.ts
│ │ │ │ ├── updateObject.ts
│ │ │ │ └── validate.ts
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ ├── objectid.ts
│ │ │ │ ├── property.ts
│ │ │ │ └── stateBuilder.ts
│ │ │ ├── objectStateProducer.ts
│ │ │ └── reducers
│ │ │ │ ├── addFormField.ts
│ │ │ │ ├── editors.ts
│ │ │ │ ├── multiEditors.ts
│ │ │ │ ├── popFocusNode.ts
│ │ │ │ ├── properties.ts
│ │ │ │ ├── removeObject.ts
│ │ │ │ ├── replaceFocusNodes.ts
│ │ │ │ ├── selectEditor.ts
│ │ │ │ ├── selectGroup.ts
│ │ │ │ ├── selectShape.ts
│ │ │ │ ├── truncateFocusNodes.ts
│ │ │ │ ├── updateObject.ts
│ │ │ │ └── validation.ts
│ │ ├── resources
│ │ │ ├── effects
│ │ │ │ └── forms
│ │ │ │ │ ├── addFormField.ts
│ │ │ │ │ ├── clearValue.ts
│ │ │ │ │ ├── createFocusNodeState.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── lib
│ │ │ │ │ └── index.ts
│ │ │ │ │ ├── removeObject.ts
│ │ │ │ │ ├── setObjectValue.ts
│ │ │ │ │ └── setPropertyObjects.ts
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ └── objectValue.ts
│ │ │ └── reducers
│ │ │ │ └── setRoot.ts
│ │ ├── shapes
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ ├── PropertyShape.ts
│ │ │ │ └── index.ts
│ │ │ └── reducers.ts
│ │ └── validation
│ │ │ ├── index.ts
│ │ │ └── reducers
│ │ │ └── setValidator.ts
│ ├── ns.ts
│ ├── package.json
│ ├── renderer.ts
│ ├── state
│ │ ├── effectsPlugin.ts
│ │ └── index.ts
│ ├── store.ts
│ └── tsconfig.json
├── hydra
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── components.ts
│ ├── index.ts
│ ├── lib
│ │ └── components
│ │ │ ├── autocomplete.ts
│ │ │ ├── instancesSelector.ts
│ │ │ ├── multiInstanceSelector.ts
│ │ │ └── searchDecorator.ts
│ ├── package.json
│ ├── test
│ │ ├── helpers
│ │ │ └── alcaeus.ts
│ │ └── lib
│ │ │ └── components
│ │ │ ├── _support.ts
│ │ │ ├── autoComplete.test.ts
│ │ │ ├── instanceSelector.test.ts
│ │ │ ├── multiInstanceSelector.test.ts
│ │ │ └── searchDecorator.test.ts
│ └── tsconfig.json
├── rdf-validate-shacl
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── testing
│ ├── CHANGELOG.md
│ ├── HydraFactory.ts
│ ├── env.ts
│ ├── index.ts
│ ├── models
│ │ ├── editors.ts
│ │ └── form.ts
│ ├── nodeFactory.ts
│ ├── package.json
│ ├── renderer.ts
│ ├── sinon.ts
│ └── util.ts
├── wc-material
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── components.ts
│ ├── components
│ │ ├── select.ts
│ │ ├── textArea.ts
│ │ └── textField.ts
│ ├── custom-elements.json
│ ├── directives
│ │ └── validity.ts
│ ├── elements
│ │ ├── SelectableMenuMixin.ts
│ │ ├── mwc-editor-toggle.ts
│ │ ├── mwc-item-lite.ts
│ │ ├── mwc-property-menu.ts
│ │ ├── mwc-shape-selector.ts
│ │ └── wc-menu.ts
│ ├── package.json
│ ├── renderer
│ │ ├── index.ts
│ │ └── tabs.ts
│ ├── test
│ │ ├── components
│ │ │ ├── booleanSelect.test.ts
│ │ │ ├── date.test.ts
│ │ │ ├── enumSelect.test.ts
│ │ │ ├── instancesSelect.test.ts
│ │ │ ├── textField.test.ts
│ │ │ └── urlEditor.test.ts
│ │ └── renderer
│ │ │ └── index.test.ts
│ └── tsconfig.json
├── wc-shoelace
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── component-extras.ts
│ ├── components.ts
│ ├── components
│ │ ├── autocomplete.ts
│ │ ├── enumSelect.ts
│ │ ├── instancesSelect.ts
│ │ ├── multiInstancesSelect.ts
│ │ └── select.ts
│ ├── custom-elements.json
│ ├── elements
│ │ ├── sh-sl-autocomplete.ts
│ │ ├── sh-sl-object.ts
│ │ ├── sh-sl-property.ts
│ │ └── sh-sl-with-lang-editor.ts
│ ├── lib
│ │ ├── components.ts
│ │ ├── difference.ts
│ │ └── handlers.ts
│ ├── package.json
│ ├── renderer
│ │ ├── boolean.ts
│ │ ├── details.ts
│ │ └── input.ts
│ ├── settings.ts
│ ├── templates.ts
│ ├── test
│ │ ├── components.test.ts
│ │ ├── components
│ │ │ ├── autocomplete.test.ts
│ │ │ ├── enumSelect.test.ts
│ │ │ ├── instancesSelect.test.ts
│ │ │ ├── multiInstancesSelect.test.ts
│ │ │ └── textField.test.ts
│ │ ├── elements
│ │ │ ├── __snapshots__
│ │ │ │ └── sh-sl-property.test.snap.js
│ │ │ ├── sh-sl-autocomplete.test.ts
│ │ │ ├── sh-sl-property.test.ts
│ │ │ └── sh-sl-with-lang-editor.test.ts
│ │ └── templates.test.ts
│ └── tsconfig.json
├── wc-vaadin
│ ├── .eslintrc.json
│ ├── CHANGELOG.md
│ ├── components.ts
│ ├── components
│ │ ├── booleanSelect.ts
│ │ ├── date.ts
│ │ ├── enumSelect.ts
│ │ ├── instancesSelect.ts
│ │ ├── text-area.ts
│ │ ├── text-field.ts
│ │ ├── url-editor.ts
│ │ └── validation.ts
│ ├── package.json
│ ├── renderer
│ │ └── accordion.ts
│ ├── test
│ │ ├── components.test.ts
│ │ └── components
│ │ │ ├── __snapshots__
│ │ │ ├── date.test.snap.js
│ │ │ ├── enumSelect.test.snap.js
│ │ │ ├── textField.test.snap.js
│ │ │ └── urlEditor.test.snap.js
│ │ │ ├── booleanSelect.test.ts
│ │ │ ├── date.test.ts
│ │ │ ├── enumSelect.test.ts
│ │ │ ├── instancesSelect.test.ts
│ │ │ ├── textField.test.ts
│ │ │ └── urlEditor.test.ts
│ └── tsconfig.json
└── wc
│ ├── CHANGELOG.md
│ ├── NativeComponents.ts
│ ├── README.md
│ ├── ShaperoneForm.ts
│ ├── components
│ ├── connect.ts
│ ├── index.ts
│ ├── lib
│ │ └── textFieldType.ts
│ ├── readonly.ts
│ └── validity.ts
│ ├── configure.ts
│ ├── custom-elements.json
│ ├── demo
│ ├── index.html
│ └── index.ts
│ ├── index.ts
│ ├── lib
│ ├── eventTarget.ts
│ └── shaperone-form.ts
│ ├── package.json
│ ├── renderer
│ ├── decorator.ts
│ ├── editor.ts
│ ├── focusNode.ts
│ ├── group.ts
│ ├── index.ts
│ ├── model.ts
│ ├── object.ts
│ └── property.ts
│ ├── store.ts
│ ├── templates.ts
│ ├── test
│ ├── NativeComponents.test.ts
│ ├── __snapshots__
│ │ └── NativeComponents.test.snap.js
│ ├── decorator.test.ts
│ ├── renderer
│ │ ├── decorator.test.ts
│ │ ├── editor.test.ts
│ │ ├── focusNode.test.ts
│ │ ├── group.test.ts
│ │ ├── model.test.ts
│ │ ├── object.test.ts
│ │ └── property.test.ts
│ ├── shaperone-form.test.ts
│ └── store.test.ts
│ ├── tsconfig.json
│ └── vite.config.js
├── tsconfig.build.json
├── tsconfig.json
├── typedoc.json
├── types
├── concat-merge
│ └── index.d.ts
├── global.d.ts
└── multiselect-combo-box
│ └── index.d.ts
├── vite.config.js
└── web-test-runner.config.mjs
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/beige-grapes-knock.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-core": minor
3 | ---
4 |
5 | Fixed typo in exported type (`MiminalEnvironment` => `MinimalEnvironment`)
6 |
--------------------------------------------------------------------------------
/.changeset/chilled-feet-hide.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-wc": minor
3 | ---
4 |
5 | Removed the `window.Shaperone.DEBUG` flag in favor of component `debug` property
6 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@1.1.0/schema.json",
3 | "changelog": "@changesets/cli/changelog",
4 | "commit": false,
5 | "linked": [],
6 | "access": "restricted",
7 | "baseBranch": "master",
8 | "updateInternalDependencies": "patch"
9 | }
--------------------------------------------------------------------------------
/.changeset/dry-shoes-cover.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-core": patch
3 | ---
4 |
5 | Fixes setting shapes from dataset
6 |
--------------------------------------------------------------------------------
/.changeset/dull-baboons-suffer.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-wc-material": patch
3 | "@hydrofoil/shaperone-wc-vaadin": patch
4 | ---
5 |
6 | Updated core package
7 |
--------------------------------------------------------------------------------
/.changeset/few-clouds-care.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-wc": patch
3 | "@hydrofoil/shaperone-wc-material": patch
4 | "@hydrofoil/shaperone-wc-vaadin": patch
5 | ---
6 |
7 | Updated `lit` to v3
8 |
--------------------------------------------------------------------------------
/.changeset/friendly-rings-repair.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-wc": patch
3 | "@hydrofoil/shaperone-wc-material": patch
4 | "@hydrofoil/shaperone-wc-vaadin": patch
5 | ---
6 |
7 | Replace directive `spread` with one from `@open-wc/lit-helpers`
8 |
--------------------------------------------------------------------------------
/.changeset/hungry-guests-boil.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-core": patch
3 | "@hydrofoil/shaperone-wc": patch
4 | ---
5 |
6 | Native components: `xsd:decimal` would not accept decimal point as a valid input.
7 |
--------------------------------------------------------------------------------
/.changeset/ninety-kiwis-kiss.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-wc": patch
3 | ---
4 |
5 | Native templates: The ability to add and remove property values
6 |
--------------------------------------------------------------------------------
/.changeset/quiet-fireants-join.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-core": patch
3 | "@hydrofoil/shaperone-wc": patch
4 | ---
5 |
6 | Improved state management by separating the state of each element instance
7 |
--------------------------------------------------------------------------------
/.changeset/seven-monkeys-beam.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-core": patch
3 | "@hydrofoil/shaperone-wc": minor
4 | ---
5 |
6 | `configure` is now async and required to register the element
7 |
--------------------------------------------------------------------------------
/.changeset/six-islands-thank.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-core": patch
3 | ---
4 |
5 | When no shape is selected and only one `sh:NodeShape` exists in graph, that shape will be used
6 |
--------------------------------------------------------------------------------
/.changeset/wild-icons-prove.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-wc": minor
3 | ---
4 |
5 | Big change how elements are configured. Global config can be set using the `configure.js` module.
6 |
7 | ```js
8 | import { configure } from '@hydrofoil/shaperone-wc'
9 |
10 | await configure(({ components, editors, renderer, validation }) => {
11 |
12 | })
13 | ```
14 |
15 | Additionally, each component can be customised further by providing calling `configure` method of the component.
16 |
17 | ```html
18 |
19 |
20 |
29 | ```
30 |
--------------------------------------------------------------------------------
/.changeset/wise-pumas-eat.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@hydrofoil/shaperone-wc": minor
3 | ---
4 |
5 | Fixes an issue that the form would not render when the node was `<>` (empty string URI)
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
23 | [*.json]
24 | indent_size = 2
25 |
26 | [*.{html,js,md}]
27 | block_comment_start = /**
28 | block_comment = *
29 | block_comment_end = */
30 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | types/
2 | node_modules/
3 | dist/
4 | *.snap.js
5 | packages/rdx/
6 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@open-wc/eslint-config",
4 | "@tpluscode"
5 | ],
6 | "env": {
7 | "browser": true
8 | },
9 | "parserOptions": {
10 | "project": "./tsconfig.json"
11 | },
12 | "settings": {
13 | "import/core-modules": [ "@captaincodeman/rdx" ]
14 | },
15 | "rules": {
16 | "import/no-extraneous-dependencies": ["error", {"devDependencies": [
17 | "**/test/**",
18 | "**/demo/**",
19 | "packages/core-tests/**"
20 | ]}],
21 | "no-shadow": "off",
22 | "max-len": ["error", 200],
23 | "wc/guard-super-call": "off",
24 | "class-methods-use-this": "off",
25 | "no-param-reassign": ["error", { "props": false }],
26 | "@typescript-eslint/no-explicit-any": "off",
27 | "lit/no-classfield-shadowing": "warn"
28 | },
29 | "overrides": [{
30 | "files": "**/test/**/*.ts",
31 | "rules": {
32 | "no-unused-expressions": "warn",
33 | "babel/no-unused-expressions": "off",
34 | "lit-a11y/accessible-name": "off"
35 | }
36 | },{
37 | "files": "packages/core-tests/**/*.ts",
38 | "rules": {
39 | "no-unused-expressions": "warn",
40 | "babel/no-unused-expressions": "off"
41 | }
42 | }, {
43 | "files": "packages/core/lib/components/*.ts",
44 | "rules": {
45 | "@typescript-eslint/no-empty-interface": "off"
46 | }
47 | }]
48 | }
49 |
--------------------------------------------------------------------------------
/.github/pr-commenter.yml:
--------------------------------------------------------------------------------
1 | comment:
2 | on-update: nothing
3 | snippets:
4 | - id: preview-url
5 | files:
6 | - 'packages/**/*'
7 | - 'demos/**/*'
8 | body: "Netlify preview deployed to [{{ url }}]({{ url }})"
9 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | release:
10 | name: Release
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout Repo
14 | uses: actions/checkout@v4
15 | with:
16 | # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
17 | fetch-depth: 0
18 |
19 | - name: Setup Node.js
20 | uses: actions/setup-node@v4
21 | with:
22 | node-version: 18
23 |
24 | - name: Install Dependencies
25 | run: npm ci
26 |
27 | - name: Create Release Pull Request or Publish to npm
28 | id: changesets
29 | uses: changesets/action@v1
30 | with:
31 | # This expects you to have a script called release which does a build for your packages and calls changeset publish
32 | publish: yarn release
33 | env:
34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.js
3 | *.js.map
4 | !*.config.js
5 | !*.conf.js
6 | coverage/
7 | !test-setup.js
8 | *.d.ts
9 | *.d.ts.map
10 | dist/api/
11 | dist/playground/
12 | dist/shaperone-form/
13 | dist/storybook/
14 | !types/**/*.d.ts
15 | *.log
16 | !*.snap.js
17 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "packages/rdx"]
2 | path = packages/rdx
3 | url = git@github.com:tpluscode/rdx.git
4 | branch = shaperone
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 | npx wsrun -m analyze
6 | git add **/custom-elements.json **/README.md
7 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 hypermedia.app
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # shaperone
2 | Form UI elements driven by SHACL Shapes
3 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - demos
3 | - packages/core-tests/test-setup.js
4 | - karma.conf.js
5 | - types
6 | - packages/*/test
7 | - packages/testing
8 | - '**/*.js'
9 | - web-test-runner.config.mjs
10 |
--------------------------------------------------------------------------------
/demos/examples/DescriptionTooltip.ts:
--------------------------------------------------------------------------------
1 | import type { TemplateResult } from 'lit'
2 | import { html } from '@hydrofoil/shaperone-wc'
3 | import type { ComponentDecorator } from '@hydrofoil/shaperone-core/models/components'
4 | import type { PropertyShape } from '@rdfine/shacl'
5 |
6 | function wrap(shape: PropertyShape, result: TemplateResult) {
7 | if (shape.description) {
8 | return html`
${result}
`
9 | }
10 |
11 | return result
12 | }
13 |
14 | export const DescriptionTooltip: ComponentDecorator = {
15 | applicableTo() {
16 | return true
17 | },
18 | decorate(component) {
19 | if ('lazyRender' in component) {
20 | return {
21 | ...component,
22 | lazyRender: async () => {
23 | const render = await component.lazyRender()
24 | return function (params, actions) {
25 | return wrap(params.property.shape, render.call(this, params, actions))
26 | }
27 | },
28 | }
29 | }
30 |
31 | return {
32 | ...component,
33 | render(params, actions) {
34 | return wrap(params.property.shape, component.render(params, actions))
35 | },
36 | }
37 | },
38 | }
39 |
--------------------------------------------------------------------------------
/demos/examples/InlineNestedShapes/index.ts:
--------------------------------------------------------------------------------
1 | import type { SingleEditorComponent } from '@hydrofoil/shaperone-wc'
2 | import { html } from '@hydrofoil/shaperone-wc'
3 | import { dash } from '@tpluscode/rdf-ns-builders'
4 | import { isResource } from 'is-graph-pointer'
5 |
6 | export const nestedForm: SingleEditorComponent = {
7 | editor: dash.DetailsEditor,
8 |
9 | render({ value, renderer, property: { shape: { node } } }) {
10 | const focusNode = value.object
11 |
12 | if (isResource(focusNode)) {
13 | return renderer.renderFocusNode({ focusNode, shape: node })
14 | }
15 |
16 | return html``
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/demos/examples/NestedShapesIndividually/components.ts:
--------------------------------------------------------------------------------
1 | import { dash, rdfs, schema } from '@tpluscode/rdf-ns-builders'
2 | import type { SingleEditorComponent } from '@hydrofoil/shaperone-wc'
3 | import { html } from '@hydrofoil/shaperone-wc'
4 | import type { GraphPointer } from 'clownface'
5 |
6 | function label(object?: GraphPointer) {
7 | return object?.out([rdfs.label, schema.name]).toArray()[0] || object?.value || ''
8 | }
9 |
10 | export const shapeLink: SingleEditorComponent = {
11 | editor: dash.DetailsEditor,
12 |
13 | render({ value }, { focusOnObjectNode }) {
14 | return html``
15 | },
16 | }
17 |
--------------------------------------------------------------------------------
/demos/examples/NestedShapesIndividually/renderer.ts:
--------------------------------------------------------------------------------
1 | import type { FormTemplate } from '@hydrofoil/shaperone-wc/templates.js'
2 | import { decorate } from '@hydrofoil/shaperone-wc/templates.js'
3 | import { html, css } from '@hydrofoil/shaperone-wc'
4 |
5 | export const topmostFocusNodeFormRenderer = decorate((form: FormTemplate): FormTemplate => {
6 | const formTemplate: FormTemplate = (renderer) => {
7 | let backButton = html``
8 | if (renderer.context.state.focusStack.length > 1) {
9 | backButton = html``
10 | }
11 |
12 | return html`${backButton}${form(renderer)}`
13 | }
14 |
15 | formTemplate.styles = css`.form-back-button {
16 | display: block;
17 | width: 115px;
18 | height: 25px;
19 | background: #4E9CAF;
20 | padding: 10px;
21 | text-align: center;
22 | border-radius: 5px;
23 | color: white;
24 | font-weight: bold;
25 | line-height: 25px;
26 | }`
27 |
28 | return formTemplate
29 | })
30 |
--------------------------------------------------------------------------------
/demos/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@hydrofoil/shaperone-playground-examples",
3 | "version": "0.3.1",
4 | "main": "index.js",
5 | "repository": "https://github.com/hypermedia-app/shaperone",
6 | "author": "Tomasz Pluskiewicz ",
7 | "license": "MIT",
8 | "type": "module",
9 | "private": true,
10 | "dependencies": {
11 | "@tpluscode/rdf-ns-builders": "^4.3.0",
12 | "@fortawesome/fontawesome-svg-core": "^1.2.32",
13 | "@fortawesome/free-solid-svg-icons": "^5.15.1",
14 | "@hydrofoil/shaperone-core": "0.12.1",
15 | "@hydrofoil/shaperone-wc": "0.8.1",
16 | "@zazuko/env": "^2.1.0",
17 | "@rdfjs-elements/lit-helpers": "^0.3.7",
18 | "@zazuko/prefixes": "^2.2.0",
19 | "is-graph-pointer": "^2",
20 | "lit": "^2.0.0",
21 | "multiselect-combo-box": "^3.0.0-alpha.2"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/demos/lit-html/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 shone-demo-lit
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/demos/lit-html/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ## Open-wc Starter App
6 |
7 | [](https://github.com/open-wc)
8 |
9 | ## Quickstart
10 |
11 | To get started:
12 |
13 | ```bash
14 | npm init @open-wc
15 | # requires node 10 & npm 6 or higher
16 | ```
17 |
18 | ## Scripts
19 |
20 | - `start` runs your app for development, reloading on file changes
21 | - `start:build` runs your app after it has been built using the build command
22 | - `build` builds your app and outputs it in your `dist` directory
23 | - `test` runs your test suite with Karma
24 | - `lint` runs the linter for your project
25 |
26 | ## Tooling configs
27 |
28 | For most of the tools, the configuration is in the `package.json` to reduce the amount of files in your project.
29 |
30 | If you customize the configuration a lot, you can consider moving them to individual files.
--------------------------------------------------------------------------------
/demos/lit-html/custom-elements.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "tags": [
4 | {
5 | "name": "shaperone-playground-lit",
6 | "description": "An application with a title and an action counter",
7 | "properties": [
8 | {
9 | "name": "title",
10 | "type": "String",
11 | "description": "The title of your application",
12 | "default": "Hey there"
13 | },
14 | {
15 | "name": "page",
16 | "type": "String",
17 | "description": "Which page to show",
18 | "default": "main"
19 | }
20 | ],
21 | "events": [],
22 | "slots": [],
23 | "cssProperties": []
24 | }
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/demos/lit-html/src/env.ts:
--------------------------------------------------------------------------------
1 | import Environment from '@zazuko/env/Environment.js'
2 | import alcaeus from 'alcaeus/Factory.js'
3 | import $rdf from '@rdfine/env'
4 | import FetchFactory from '@rdfjs/fetch-lite/Factory.js'
5 |
6 | export default new Environment([alcaeus(), FetchFactory], { parent: $rdf })
7 |
--------------------------------------------------------------------------------
/demos/lit-html/src/menu.ts:
--------------------------------------------------------------------------------
1 | export interface Menu {
2 | id?: string
3 | type?: 'layout' | 'renderer' | 'format' | 'components' | 'editorChoice' | 'labs'
4 | text?: string
5 | checked?: boolean
6 | children?: Menu[]
7 | component?: string | HTMLElement
8 | }
9 |
10 | export function updateMenu(menu: Menu, type: Menu['type'], text: string | undefined): Menu {
11 | let { checked } = menu
12 | if (menu.type === type) {
13 | checked = menu.text === text
14 | }
15 |
16 | return {
17 | ...menu,
18 | checked,
19 | children: menu.children?.map(child => updateMenu(child, type, text)),
20 | }
21 | }
22 |
23 | export function updateComponent(menu: Menu, id: string, newComponent: string | HTMLElement): Menu {
24 | let { component } = menu
25 | if (menu.id === id) {
26 | component = newComponent
27 | }
28 |
29 | return {
30 | ...menu,
31 | component,
32 | children: menu.children?.map(child => updateComponent(child, id, newComponent)),
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/demos/lit-html/src/state/config.ts:
--------------------------------------------------------------------------------
1 | import * as models from './models/index.js'
2 |
3 | export const config = {
4 | models,
5 | }
6 |
--------------------------------------------------------------------------------
/demos/lit-html/src/state/models/components.ts:
--------------------------------------------------------------------------------
1 | import { createModel } from '@captaincodeman/rdx'
2 |
3 | export interface ComponentsState {
4 | components: 'native' | 'material' | 'vaadin' | 'shoelace'
5 | disableEditorChoice: boolean
6 | }
7 |
8 | export const componentsSettings = createModel({
9 | state: {
10 | components: 'native',
11 | disableEditorChoice: false,
12 | },
13 | reducers: {
14 | switchComponents(state, components: ComponentsState['components']) {
15 | switch (components) {
16 | case 'material':
17 | case 'native':
18 | case 'vaadin':
19 | case 'shoelace':
20 | return { ...state, components }
21 | default:
22 | return state
23 | }
24 | },
25 | setEditorChoice(state, disableEditorChoice: boolean) {
26 | return {
27 | ...state,
28 | disableEditorChoice,
29 | }
30 | },
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/demos/lit-html/src/state/models/index.ts:
--------------------------------------------------------------------------------
1 | export { componentsSettings } from './components.js'
2 | export { resource } from './resource.js'
3 | export { shape } from './shape.js'
4 | export { rendererSettings } from './renderer.js'
5 | export { playground } from './playground.js'
6 |
--------------------------------------------------------------------------------
/demos/lit-html/src/state/models/renderer.ts:
--------------------------------------------------------------------------------
1 | import { createModel } from '@captaincodeman/rdx'
2 |
3 | export interface RendererState {
4 | grouping: 'none' | 'material tabs' | 'vaadin accordion'
5 | nesting: 'none' | 'always one' | 'inline'
6 | labs: {
7 | xone?: boolean
8 | errorSummary?: boolean
9 | }
10 | }
11 |
12 | export const rendererSettings = createModel({
13 | state: {
14 | grouping: 'none',
15 | nesting: 'none',
16 | labs: {},
17 | },
18 | reducers: {
19 | switchNesting(state, nesting: RendererState['nesting']) {
20 | return {
21 | ...state,
22 | nesting,
23 | }
24 | },
25 | switchLayout(state, grouping: RendererState['grouping']) {
26 | return {
27 | ...state,
28 | grouping,
29 | }
30 | },
31 | toggleLab({ labs = {}, ...state }, { lab, value } : {lab: keyof Required['labs']; value?: boolean}) {
32 | return {
33 | ...state,
34 | labs: {
35 | ...labs,
36 | [lab]: typeof value !== 'undefined' ? value : !labs[lab],
37 | },
38 | }
39 | },
40 | },
41 | })
42 |
--------------------------------------------------------------------------------
/demos/lit-html/src/state/store.ts:
--------------------------------------------------------------------------------
1 | import type { ModelStore, StoreDispatch, StoreState } from '@captaincodeman/rdx'
2 | import { createStore, persist } from '@captaincodeman/rdx'
3 | import { config } from './config.js'
4 |
5 | export type State = StoreState
6 | export type Dispatch = StoreDispatch
7 | export type Store = ModelStore
8 |
9 | export const store = (() => {
10 | const store = persist(createStore(config), {
11 | persist(state: State) {
12 | const { shape, resource } = state
13 | const { serialized, format, options } = shape
14 |
15 | return {
16 | shape: {
17 | serialized,
18 | format,
19 | quads: [],
20 | dataset: undefined,
21 | options,
22 | shapes: [],
23 | },
24 | resource: {
25 | format: resource.format,
26 | serialized: resource.serialized,
27 | prefixes: resource.prefixes,
28 | selectedResource: resource.selectedResource,
29 | resourcesToSelect: [],
30 | pointer: undefined,
31 | graph: undefined,
32 | quads: [],
33 | },
34 | rendererSettings: {
35 | ...state.rendererSettings,
36 | },
37 | componentsSettings: {
38 | ...state.componentsSettings,
39 | },
40 | playground: {
41 | ...state.playground,
42 | },
43 | }
44 | },
45 | })
46 |
47 | return () => store
48 | })()
49 |
--------------------------------------------------------------------------------
/demos/lit-html/vite.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies,import/no-relative-packages */
2 | import topLevelAwait from 'vite-plugin-top-level-await'
3 | import { defineConfig, mergeConfig } from 'vite'
4 | import config from '../../vite.config.js'
5 |
6 | export default defineConfig(({ command }) => {
7 | if (command === 'build') {
8 | return mergeConfig(config, {
9 | base: '/playground/',
10 | plugins: [topLevelAwait()],
11 | })
12 | }
13 |
14 | return mergeConfig(config, {
15 | plugins: [topLevelAwait()],
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/demos/storybook/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import { join, dirname } from 'node:path'
2 | import { StorybookConfig } from '@storybook/web-components-vite'
3 |
4 | /**
5 | * This function is used to resolve the absolute path of a package.
6 | * It is needed in projects that use Yarn PnP or are set up within a monorepo.
7 | */
8 | const getAbsolutePath = (value: string) => {
9 | return dirname(require.resolve(join(value, 'package.json')))
10 | }
11 |
12 | const config: StorybookConfig = {
13 | stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
14 |
15 | addons: [
16 | getAbsolutePath('@storybook/addon-links'),
17 | getAbsolutePath('@storybook/addon-essentials'),
18 | ],
19 |
20 | framework: {
21 | name: getAbsolutePath('@storybook/web-components-vite'),
22 | options: {},
23 | },
24 |
25 | staticDirs: [
26 | '../shapes',
27 | '../../../node_modules/@shoelace-style/'
28 | ],
29 |
30 | docs: {},
31 | }
32 | export default config
33 |
--------------------------------------------------------------------------------
/demos/storybook/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/demos/storybook/data/simple/john-doe.ttl:
--------------------------------------------------------------------------------
1 | PREFIX schema:
2 | BASE
3 |
4 |
5 | a schema:Person ;
6 | schema:givenName "John" ;
7 | schema:familyName "Doe" ;
8 | .
9 |
--------------------------------------------------------------------------------
/demos/storybook/global.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*?raw' {
2 | const content: string
3 | export default content
4 | }
5 |
--------------------------------------------------------------------------------
/demos/storybook/lib/env.ts:
--------------------------------------------------------------------------------
1 | import Environment from '@zazuko/env/Environment.js'
2 | import FetchFactory from '@rdfjs/fetch-lite/Factory.js'
3 | import rdf from '@zazuko/env'
4 | import formats from '@rdfjs-elements/formats-pretty'
5 |
6 | const env = new Environment([FetchFactory], { parent: rdf })
7 |
8 | env.formats.import(formats)
9 | export default env
10 |
--------------------------------------------------------------------------------
/demos/storybook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shaperone-stories",
3 | "version": "0.0.0",
4 | "private": "true",
5 | "type": "module",
6 | "scripts": {
7 | "start": "storybook dev -p 6006 --no-open",
8 | "build": "storybook build -o ../../dist/storybook"
9 | },
10 | "dependencies": {
11 | "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
12 | "@hydrofoil/shaperone-wc": "^0.8.0",
13 | "@hydrofoil/shaperone-wc-shoelace": "^0.4.1",
14 | "@rdfjs/fetch-lite": "^3.3.0",
15 | "@rdfjs-elements/rdf-snippet": "^0.4.5",
16 | "@rdfjs-elements/formats-pretty": "^0.6.7",
17 | "@shoelace-style/shoelace": "^2.15.0",
18 | "@storybook/addon-actions": "^8.4.5",
19 | "@storybook/addon-essentials": "^8.4.5",
20 | "@storybook/addon-links": "^8.4.5",
21 | "@storybook/addon-storysource": "^8.4.5",
22 | "@storybook/blocks": "^8.4.5",
23 | "@storybook/components": "^8.4.5",
24 | "@storybook/web-components": "^8.4.5",
25 | "@storybook/web-components-vite": "^8.4.5",
26 | "@vitejs/plugin-react": "^4.3.3",
27 | "@zazuko/env": "^2",
28 | "lit": "^3",
29 | "onetime": "^7.0.0",
30 | "react-syntax-highlighter": "^15.6.1",
31 | "rollup-plugin-polyfill-node": "^0.13.0",
32 | "storybook": "^8.4.5",
33 | "string-to-stream": "^3.0.1"
34 | },
35 | "devDependencies": {
36 | "@types/react-syntax-highlighter": "^15.5.13"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/demos/storybook/shapes/simple/datatypes.ttl:
--------------------------------------------------------------------------------
1 | PREFIX xsd:
2 | PREFIX rdf:
3 | PREFIX schema:
4 | PREFIX sh:
5 |
6 | <>
7 | a sh:NodeShape ;
8 | sh:property
9 | [
10 | sh:name "Name" ;
11 | sh:path schema:name ;
12 | sh:datatype rdf:langString ;
13 | sh:minCount 1 ;
14 | sh:maxCount 1 ;
15 | ],
16 | [
17 | sh:name "Base Salary" ;
18 | sh:path schema:baseSalary ;
19 | sh:datatype xsd:decimal ;
20 | sh:minCount 1 ;
21 | sh:maxCount 1 ;
22 | ],
23 | [
24 | sh:name "Age" ;
25 | sh:path schema:age ;
26 | sh:datatype xsd:integer ;
27 | sh:minCount 1 ;
28 | sh:maxCount 1 ;
29 | ] ;
30 | .
31 |
--------------------------------------------------------------------------------
/demos/storybook/shapes/simple/first-last.ttl:
--------------------------------------------------------------------------------
1 | PREFIX ex:
2 | PREFIX dash:
3 | PREFIX schema:
4 | PREFIX sh:
5 |
6 | ex:PersonShape
7 | a sh:NodeShape ;
8 | sh:targetClass schema:Person ;
9 | sh:property
10 | [
11 | sh:name "First Name" ;
12 | sh:path schema:givenName ;
13 | dash:editor dash:TextFieldEditor ;
14 | sh:minCount 1 ;
15 | sh:maxCount 1 ;
16 | ],
17 | [
18 | sh:name "Last Name" ;
19 | sh:path schema:familyName ;
20 | dash:editor dash:TextFieldEditor ;
21 | sh:minCount 1 ;
22 | sh:maxCount 1 ;
23 | ] ;
24 | .
25 |
--------------------------------------------------------------------------------
/demos/storybook/shapes/simple/unrestricted.ttl:
--------------------------------------------------------------------------------
1 | PREFIX ex:
2 | PREFIX dash:
3 | PREFIX schema:
4 | PREFIX sh:
5 |
6 | # Like first-last.ttl, but without the minCount and maxCount constraints
7 | ex:TestShape
8 | a sh:NodeShape ;
9 | sh:targetClass schema:Person ;
10 | sh:property
11 | [
12 | sh:name "First Name" ;
13 | sh:path schema:givenName ;
14 | dash:editor dash:TextFieldEditor ;
15 | ],
16 | [
17 | sh:name "Last Name" ;
18 | sh:path schema:familyName ;
19 | dash:editor dash:TextFieldEditor ;
20 | ],
21 | [
22 | sh:name "Occupation" ;
23 | sh:path schema:jobTitle ;
24 | dash:editor dash:TextFieldEditor ;
25 | ] ;
26 | .
27 |
--------------------------------------------------------------------------------
/demos/storybook/stories/Shoelace.stories.ts:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj as Story } from '@storybook/web-components'
2 | import * as shoelace from '@hydrofoil/shaperone-wc-shoelace/components.js'
3 | import * as templates from '@hydrofoil/shaperone-wc-shoelace/templates.js'
4 | import type { ConfigCallback } from '@hydrofoil/shaperone-wc/configure.js'
5 | import { render } from './render.js'
6 | import firstLast from '../shapes/simple/first-last.ttl?raw'
7 |
8 | const configure: ConfigCallback = ({ components, renderer }) => {
9 | components.pushComponents(shoelace)
10 | renderer.setTemplates(templates)
11 | }
12 |
13 | const meta: Meta = {
14 | component: 'shaperone-form',
15 | argTypes: {
16 | focusNode: {
17 | control: 'text',
18 | },
19 | shape: {
20 | control: 'text',
21 | },
22 | prefixes: {
23 | control: 'text',
24 | },
25 | },
26 | args: {
27 | prefixes: 'schema',
28 | },
29 | }
30 |
31 | export default meta
32 |
33 | /**
34 | * Without setting the data graph, the form will be empty and a `<>` IRI used for focus node.
35 | */
36 | export const EmptyDataGraph: Story = {
37 | name: 'Empty data graph',
38 | args: {
39 | shapes: firstLast,
40 | shape: 'http://example.org/PersonShape',
41 | },
42 | loaders: [
43 | async () => ({
44 | configure,
45 | }),
46 | ],
47 | render,
48 | }
49 |
--------------------------------------------------------------------------------
/demos/storybook/stories/render.ts:
--------------------------------------------------------------------------------
1 | import type { GraphPointer } from 'clownface'
2 | import type { WebComponentsRenderer } from '@storybook/web-components'
3 | import type { ArgsStoryFn } from '@storybook/csf'
4 | import { html } from 'lit'
5 |
6 | import '../lib/shaperone-demo.js'
7 |
8 | interface Args {
9 | focusNode?: string
10 | prefixes?: string
11 | shapes?: string
12 | data?: string
13 | debug?: boolean
14 | }
15 |
16 | const render: ArgsStoryFn = function ({ focusNode, prefixes, debug, ...raw }: Args, { loaded: { shapes, data, configure } }) {
17 | let resource: GraphPointer | undefined
18 | if (focusNode) {
19 | resource = data.namedNode(focusNode)
20 | }
21 | return html`
22 |
23 |
24 | `
25 | }
26 |
27 | export { render }
28 |
--------------------------------------------------------------------------------
/demos/storybook/vite.config.js:
--------------------------------------------------------------------------------
1 | import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
2 | import rollupNodePolyFill from 'rollup-plugin-polyfill-node'
3 | import react from '@vitejs/plugin-react'
4 |
5 | export default {
6 | define: {
7 | global: 'window',
8 | },
9 | resolve: {
10 | alias: {
11 | stream: 'readable-stream',
12 | zlib: 'browserify-zlib',
13 | util: 'util',
14 | },
15 | },
16 | optimizeDeps: {
17 | esbuildOptions: {
18 | plugins: [
19 | NodeGlobalsPolyfillPlugin({
20 | process: true,
21 | buffer: true,
22 | }),
23 | ],
24 | },
25 | },
26 | build: {
27 | rollupOptions: {
28 | plugins: [rollupNodePolyFill()],
29 | },
30 | },
31 | plugins: [
32 | react({
33 | jsxRuntime: 'automatic',
34 | }),
35 | ],
36 | }
37 |
--------------------------------------------------------------------------------
/dist/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypermedia-app/shaperone/824456560ac22410f577bcde09b7dfde79211ca7/dist/.nojekyll
--------------------------------------------------------------------------------
/dist/_coverpage.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # @hydrofoil/shaperone
4 |
5 | > SHACL Form generator
6 |
7 | - Compatible with standard RDF/JS tooling
8 | - Highly customizable
9 | - Using modern web technologies
10 |
11 | [Get started](overview)
12 | [GitHub](https://github.com/hypermedia-app/shaperone)
13 | [JS API](/api)
14 | [`shaperone-form`](/shaperone-form)
15 | [Playground](/playground)
16 |
--------------------------------------------------------------------------------
/dist/_media/GitHub-Mark-32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypermedia-app/shaperone/824456560ac22410f577bcde09b7dfde79211ca7/dist/_media/GitHub-Mark-32px.png
--------------------------------------------------------------------------------
/dist/_media/anatomy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypermedia-app/shaperone/824456560ac22410f577bcde09b7dfde79211ca7/dist/_media/anatomy.png
--------------------------------------------------------------------------------
/dist/_media/configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypermedia-app/shaperone/824456560ac22410f577bcde09b7dfde79211ca7/dist/_media/configuration.png
--------------------------------------------------------------------------------
/dist/_media/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypermedia-app/shaperone/824456560ac22410f577bcde09b7dfde79211ca7/dist/_media/logo.png
--------------------------------------------------------------------------------
/dist/_media/multi-select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypermedia-app/shaperone/824456560ac22410f577bcde09b7dfde79211ca7/dist/_media/multi-select.png
--------------------------------------------------------------------------------
/dist/_media/stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypermedia-app/shaperone/824456560ac22410f577bcde09b7dfde79211ca7/dist/_media/stack.png
--------------------------------------------------------------------------------
/dist/_media/star-rating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypermedia-app/shaperone/824456560ac22410f577bcde09b7dfde79211ca7/dist/_media/star-rating.png
--------------------------------------------------------------------------------
/dist/_sidebar.md:
--------------------------------------------------------------------------------
1 | * Getting started
2 | * [Intro](overview.md "Shaperone | Intro")
3 | * [Getting Started](getting-started.md "Shaperone | Getting Started")
4 | * [Configuration](configuration.md "Shaperone | Configuration")
5 | * [Anatomy of shaperone](anatomy.md "Shaperone | Anatomy")
6 | * Editors
7 | * [Overview](editors.md "Shaperone | Editors")
8 | * [DASH](editors/dash.md "Shaperone | DASH")
9 | * [Matchers](editors/matchers.md "Shaperone | Editor matchers")
10 | * [Metadata](editors/metadata.md "Shaperone | Editor metadata")
11 | * [Core library](core.md "Shaperone | Core")
12 | * [Advanced shapes](advanced.md "Shaperone | Advanced")
13 | * [Validation](validation.md "Shaperone | Validation")
14 | * Renderer
15 | * [Overview](renderer/core.md)
16 | * [Web Components](renderer/web-components.md)
17 | * Web Components
18 | * [DASH](components/dash.md "Shaperone | Component | DASH")
19 | * [Design systems](components/design-systems/ "Shaperone | Components | Design system")
20 | * [Material Design](components/design-systems/material.md "Shaperone | Components | Material Design")
21 | * [Vaadin](components/design-systems/vaddin.md "Shaperone | Components | Vaadin")
22 | * [Create your own](components/implement.md "Shaperone | Implementing components")
23 | * Extensions
24 | * [Hydra](extensions/hydra.md "Shaperone | Hydra")
25 | * [Plugins](extensions/plugins.md "Shaperone | Plugins")
26 | * [API](/api)
27 |
--------------------------------------------------------------------------------
/dist/extensions/plugins.md:
--------------------------------------------------------------------------------
1 | # Plugins
2 |
3 | A low-level feature allows adding custom functionality directly to the state store. This attaches directly to the
4 | [rdx](https://github.com/CaptainCodeman/rdx) library and allows full access to action invoked on the store.
5 |
6 | ## Setup
7 |
8 | Use `addPlugin` to have it added to the store. Make sure to call this before a form is initialised on a page.
9 |
10 | ```js
11 | import { addPlugin } from '@hydrofoil/shaperone-core/store'
12 |
13 | const loggerPlugin = {
14 | // a plugin can have its own state
15 | model: {
16 | state: { enabled: false },
17 | reducers: {
18 | toggle(state) {
19 | return { ...state, enabled: !state.enabled }
20 | }
21 | },
22 | // and (async) effects which listen and call other models, or do fancy stuff
23 | effects(store) {
24 | const dispatch = store.getDispatch()
25 |
26 | return {
27 | 'forms/initObjectValue'(arg) {
28 | console.log('Initialized property', arg)
29 |
30 | dispatch.forms.updateComponentState({
31 | ...arg,
32 | newState: {
33 | observed: true
34 | }
35 | })
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
42 | addPlugins({
43 | loggerPlugin
44 | })
45 | ```
46 |
47 | ## See more
48 |
49 | Unfortunately, the plugin feature is not well documented. See the built-in plugins for inspiration: [effects](https://github.com/CaptainCodeman/rdx/blob/master/src/effectsPlugin.ts),
50 | [routing](https://github.com/CaptainCodeman/rdx/blob/master/src/routingPlugin.ts)
51 |
--------------------------------------------------------------------------------
/dist/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | To add a most basic form to you app create a `` element in your HTML and set its `shapes` property with a [RDF/JS Dataset](https://rdf.js.org/dataset-spec/#datasetcore-interface) or [clownface](https://zazuko.github.io/clownface) graph pointer.
4 |
5 | ```js
6 | // 1. Import the Web Component's package
7 | import '@hydrofoil/shaperone-wc/shaperone-form.js'
8 |
9 | // 2. Load or create shape's RDF representation
10 | const shape = fetchShape()
11 |
12 | // 3. Set the shape to the element
13 | document.querySelector('shaperone-form').shapes = shape
14 | ```
15 |
16 | To access the contents of a form get the `.resource` property which again returns a [clownface](https://zazuko.github.io/clownface) graph pointer object.
17 |
18 | > [!TIP]
19 | > Naturally, the `.resource` property also has a getter. If not set, the form will be initialized with an empty string `<>` named node.
20 |
21 | Below you'll find a running example rendering a very simple form. It also adds a simple button which will open the resulting RDF in a new tab, serialized as turtle.
22 |
23 |
29 |
--------------------------------------------------------------------------------
/dist/renderer/core.md:
--------------------------------------------------------------------------------
1 | # Renderer
2 |
3 | While the core library does not provide any rendering code, it defines a set of base interfaces for renderers. At the highest level a renderer is a simple interface, with but a single method to render the entire form.
4 |
5 | ```typescript
6 | /**
7 | * @module @hydrofoil/shaperone-core/renderer
8 | */
9 |
10 | export interface RenderContext {
11 | form: symbol
12 | editors: EditorsState
13 | state: FormState
14 | components: ComponentsState
15 | dispatch: Dispatch
16 | shapes: NodeShape[]
17 | }
18 |
19 |
20 | export interface Renderer {
21 | render(params: RenderContext): TRenderResult
22 | }
23 | ```
24 |
25 | However, the assumption is that the implementation breaks up the rendering in at least the following parts:
26 |
27 | 1. [The form](/api/interfaces/_hydrofoil_shaperone_core_renderer.formrenderer.html) itself at the top level
28 | 2. [Focus Node](/api/interfaces/_hydrofoil_shaperone_core_renderer.focusnoderenderer.html)
29 | 3. (Optionally) [Group Shapes](/api/interfaces/_hydrofoil_shaperone_core_renderer.grouprenderer.html)
30 | 4. [Property Shapes](/api/interfaces/_hydrofoil_shaperone_core_renderer.propertyrenderer.html)
31 | 5. [Objects](/api/interfaces/_hydrofoil_shaperone_core_renderer.objectrenderer.html)
32 |
33 | Each subsequent renderer level expands the one above by adding the context objects and mutation callbacks, called `actions`. For example, a `PropertyRenderer` combines its parents and adds the property state object and functions.
34 |
35 | Although it is up to the implementors to follow this pattern exactly or not, ultimately the components' render method require an instance of `ObjectRenderer`, which represents the object's position in the graph.
36 |
--------------------------------------------------------------------------------
/dist/renderer/web-components.md:
--------------------------------------------------------------------------------
1 | # Rendering Web Components
2 |
3 | TBD
4 |
--------------------------------------------------------------------------------
/dist/variables.json:
--------------------------------------------------------------------------------
1 | {
2 | "playground": "http://localhost:8080"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/core-tests/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @hydrofoil/shaperone-core-tests
2 |
3 | ## 0.1.1
4 |
5 | ### Patch Changes
6 |
7 | - 40dd516: Build: fixes some incorrect imports in generated d.ts files
8 |
9 | ## 0.1.0
10 |
11 | ### Minor Changes
12 |
13 | - e76afd5: Use a centralised RDF/JS Environment
14 |
15 | ### Patch Changes
16 |
17 | - e76afd5: Update RDF/JS-related dependencies (closes #300)
18 |
19 | ## 0.0.1
20 |
21 | ### Patch Changes
22 |
23 | - 61ac785: Update `@tpluscode/rdf-ns-builders` to v2
24 |
--------------------------------------------------------------------------------
/packages/core-tests/lib/property.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import $rdf from '@shaperone/testing/env.js'
4 | import { sh, xsd } from '@tpluscode/rdf-ns-builders'
5 | import { createTerm } from '@hydrofoil/shaperone-core/lib/property.js'
6 | import { propertyShape } from '@shaperone/testing/util.js'
7 |
8 | describe('core/lib/property', () => {
9 | describe('createTerm', () => {
10 | it('creates named node when property has sh:IRI kind', () => {
11 | // given
12 | const pointer = $rdf.clownface().blankNode()
13 | const property = {
14 | shape: propertyShape(pointer, {
15 | [sh.nodeKind.value]: sh.IRI,
16 | }),
17 | }
18 |
19 | // when
20 | const term = createTerm($rdf, property, 'http://foo/bar')
21 |
22 | // then
23 | expect(term.value).to.equal('http://foo/bar')
24 | })
25 |
26 | it('creates typed literal when property has sh:datatype', () => {
27 | // given
28 | const pointer = $rdf.clownface().blankNode()
29 | const property = {
30 | shape: propertyShape(pointer),
31 | datatype: xsd.int,
32 | }
33 |
34 | // when
35 | const term = createTerm($rdf, property, '41')
36 |
37 | // then
38 | expect(term).to.deep.equal($rdf.literal('41', xsd.int))
39 | })
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/packages/core-tests/models/editors/effects/loadDash.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import { testStore } from '@shaperone/testing/models/form.js'
4 | import type { Store } from '@hydrofoil/shaperone-core/state'
5 | import { loadDash } from '@hydrofoil/shaperone-core/models/editors/effects/index.js'
6 |
7 | describe('models/editors/effects/loadDash', () => {
8 | let store: Store
9 |
10 | beforeEach(() => {
11 | store = testStore()
12 | })
13 |
14 | it('does not add metadata twice', async () => {
15 | // given
16 | const effect = loadDash(store)
17 | await effect()
18 |
19 | // when
20 | await effect()
21 |
22 | // expect
23 | const dispatch = store.getDispatch()
24 | expect(dispatch.editors.addMatchers).to.have.been.calledOnce
25 | expect(dispatch.editors.addMetadata).to.have.been.calledOnce
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/packages/core-tests/models/forms/effects/removeObject.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import $rdf from '@shaperone/testing/env.js'
4 | import { dash, schema, sh } from '@tpluscode/rdf-ns-builders'
5 | import { testFocusNodeState, testPropertyState, testStore } from '@shaperone/testing/models/form.js'
6 | import type { Store } from '@hydrofoil/shaperone-core/state'
7 | import { removeObject } from '@hydrofoil/shaperone-core/models/forms/effects/removeObject.js'
8 | import { nodeShape, propertyShape } from '@shaperone/testing/util.js'
9 |
10 | describe('models/forms/effects/removeObject', () => {
11 | let store: Store
12 |
13 | beforeEach(() => {
14 | store = testStore()
15 | })
16 |
17 | it('dispatches update of property linked by sh:equals', () => {
18 | // given
19 | const focusNode = $rdf.clownface().namedNode('foo')
20 | const shapesGraph = $rdf.clownface()
21 | const property = propertyShape(shapesGraph.blankNode(), {
22 | path: schema.name,
23 | })
24 | const synced = propertyShape(shapesGraph.blankNode(), {
25 | [dash.hidden.value]: true,
26 | path: schema.givenName,
27 | [sh.equals.value]: schema.name,
28 | })
29 | store.getState().form.focusNodes = testFocusNodeState(focusNode, {
30 | shape: nodeShape(shapesGraph.blankNode()),
31 | properties: [testPropertyState(property.pointer), testPropertyState(synced.pointer)],
32 | })
33 |
34 | // when
35 | removeObject(store)({
36 | focusNode,
37 | property,
38 | })
39 |
40 | // then
41 | const dispatch = store.getDispatch()
42 | expect(dispatch.form.setPropertyObjects).to.have.been.called
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/packages/core-tests/models/forms/effects/replaceObjects.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import $rdf from '@shaperone/testing/env.js'
3 | import { expect } from 'chai'
4 | import type { sinon } from '@shaperone/testing'
5 | import { testStore } from '@shaperone/testing/models/form.js'
6 | import { replaceObjects } from '@hydrofoil/shaperone-core/models/forms/effects/replaceObjects.js'
7 | import type { Store } from '@hydrofoil/shaperone-core/state'
8 | import { propertyShape } from '@shaperone/testing/util.js'
9 |
10 | describe('models/forms/effects/replaceObjects', () => {
11 | let store: Store
12 |
13 | beforeEach(() => {
14 | store = testStore()
15 | })
16 |
17 | it('dispatches setPropertyObjects with pointers from resource graph', () => {
18 | // given
19 | const { dataset } = store.getState().resources.graph!
20 | const property = propertyShape()
21 | const focusNode = $rdf.clownface().blankNode()
22 | const terms = [
23 | $rdf.literal('a'),
24 | $rdf.literal('b'),
25 | $rdf.literal('c'),
26 | ]
27 |
28 | // when
29 | replaceObjects(store)({
30 | property,
31 | focusNode,
32 | terms,
33 | })
34 |
35 | // then
36 | const dispatch = store.getDispatch()
37 | const [call] = (dispatch.form.setPropertyObjects as sinon.SinonStub).getCalls()
38 | expect(call.lastArg.objects.dataset === dataset)
39 | })
40 | })
41 |
--------------------------------------------------------------------------------
/packages/core-tests/models/forms/effects/selectShape.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import $rdf from '@shaperone/testing/env.js'
3 | import { expect } from 'chai'
4 | import type { sinon } from '@shaperone/testing'
5 | import type { NodeShape } from '@rdfine/shacl'
6 | import { testStore } from '@shaperone/testing/models/form.js'
7 | import { selectShape } from '@hydrofoil/shaperone-core/models/forms/effects/selectShape.js'
8 | import type { Store } from '@hydrofoil/shaperone-core/state'
9 |
10 | describe('models/forms/effects/selectShape', () => {
11 | let store: Store
12 |
13 | beforeEach(() => {
14 | store = testStore()
15 | })
16 |
17 | it('creates state with selected shape', () => {
18 | // given
19 | const shape = {} as NodeShape
20 | const focusNode = $rdf.clownface().blankNode()
21 |
22 | // when
23 | selectShape(store)({
24 | focusNode,
25 | shape,
26 | })
27 |
28 | // then
29 | const dispatch = store.getDispatch()
30 | const [call] = (dispatch.form.createFocusNodeState as sinon.SinonStub).getCalls()
31 | expect(call.lastArg.shape === shape)
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/packages/core-tests/models/forms/effects/setObjectValue.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import $rdf from '@shaperone/testing/env.js'
4 | import { dash, schema, sh } from '@tpluscode/rdf-ns-builders'
5 | import { testStore, testFocusNodeState, testPropertyState } from '@shaperone/testing/models/form.js'
6 | import type { Store } from '@hydrofoil/shaperone-core/state'
7 | import { setObjectValue } from '@hydrofoil/shaperone-core/models/forms/effects/setObjectValue.js'
8 | import { nodeShape, propertyShape } from '@shaperone/testing/util.js'
9 |
10 | describe('models/forms/effects/setObjectValue', () => {
11 | let store: Store
12 |
13 | beforeEach(() => {
14 | store = testStore()
15 | })
16 |
17 | it('dispatches update of property linked by sh:equals', () => {
18 | // given
19 | const focusNode = $rdf.clownface().namedNode('foo')
20 | const shapesGraph = $rdf.clownface()
21 | const property = propertyShape(shapesGraph.blankNode(), {
22 | path: schema.name,
23 | })
24 | const synced = propertyShape(shapesGraph.blankNode(), {
25 | [dash.hidden.value]: true,
26 | path: schema.givenName,
27 | [sh.equals.value]: schema.name,
28 | })
29 | store.getState().form.focusNodes = testFocusNodeState(focusNode, {
30 | shape: nodeShape(shapesGraph.blankNode()),
31 | properties: [testPropertyState(property.pointer), testPropertyState(synced.pointer)],
32 | })
33 |
34 | // when
35 | setObjectValue(store)({
36 | focusNode,
37 | property,
38 | })
39 |
40 | // then
41 | const dispatch = store.getDispatch()
42 | expect(dispatch.form.setPropertyObjects).to.have.been.called
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/packages/core-tests/models/forms/effects/setPropertyObjects.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import $rdf from '@shaperone/testing/env.js'
4 | import { dash, schema, sh } from '@tpluscode/rdf-ns-builders'
5 | import { testFocusNodeState, testPropertyState, testStore } from '@shaperone/testing/models/form.js'
6 | import type { Store } from '@hydrofoil/shaperone-core/state'
7 | import { setPropertyObjects } from '@hydrofoil/shaperone-core/models/forms/effects/setPropertyObjects.js'
8 | import { nodeShape, propertyShape } from '@shaperone/testing/util.js'
9 |
10 | describe('models/forms/effects/setPropertyObjects', () => {
11 | let store: Store
12 |
13 | beforeEach(() => {
14 | store = testStore()
15 | })
16 |
17 | it('dispatches update of property linked by sh:equals', () => {
18 | // given
19 | const focusNode = $rdf.clownface().namedNode('foo')
20 | const shapesGraph = $rdf.clownface()
21 | const property = propertyShape(shapesGraph.blankNode(), {
22 | path: schema.name,
23 | })
24 | const synced = propertyShape(shapesGraph.blankNode(), {
25 | [dash.hidden.value]: true,
26 | path: schema.givenName,
27 | [sh.equals.value]: schema.name,
28 | })
29 | store.getState().form.focusNodes = testFocusNodeState(focusNode, {
30 | shape: nodeShape(shapesGraph.blankNode()),
31 | properties: [testPropertyState(property.pointer), testPropertyState(synced.pointer)],
32 | })
33 |
34 | // when
35 | setPropertyObjects(store)({
36 | focusNode,
37 | property,
38 | })
39 |
40 | // then
41 | const dispatch = store.getDispatch()
42 | expect(dispatch.form.setPropertyObjects).to.have.been.called
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/packages/core-tests/models/forms/effects/shapes/setGraph.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import $rdf from '@shaperone/testing/env.js'
3 | import { rdf, sh } from '@tpluscode/rdf-ns-builders'
4 | import { expect } from 'chai'
5 | import { testStore } from '@shaperone/testing/models/form.js'
6 | import setGraph from '@hydrofoil/shaperone-core/models/forms/effects/shapes/setGraph.js'
7 | import type { Store } from '@hydrofoil/shaperone-core/state'
8 |
9 | const ex = $rdf.namespace('http://example.com/')
10 |
11 | describe('models/forms/effects/shapes/setGraph', () => {
12 | let store: Store
13 |
14 | beforeEach(() => {
15 | store = testStore()
16 | })
17 |
18 | it('creates focus nodes state for focus stack', () => {
19 | // given
20 | const resourceGraph = $rdf.clownface()
21 | const shapesGraph = $rdf.clownface()
22 | shapesGraph.node(ex.Shape).addOut(rdf.type, sh.Shape).addOut(sh.targetNode, [ex.Foo, ex.Bar])
23 | const formState = store.getState().form
24 | formState.focusStack = [
25 | resourceGraph.node(ex.Foo),
26 | resourceGraph.node(ex.Bar),
27 | ]
28 |
29 | // when
30 | setGraph(store)()
31 |
32 | // then
33 | const spy = store.getDispatch().form.createFocusNodeState as sinon.SinonSpy
34 | expect(spy.getCalls().map(c => c.firstArg)).to.containSubset([
35 | { focusNode: resourceGraph.node(ex.Foo) },
36 | { focusNode: resourceGraph.node(ex.Bar) },
37 | ])
38 | })
39 | })
40 |
--------------------------------------------------------------------------------
/packages/core-tests/models/forms/effects/validate.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import $rdf from '@shaperone/testing/env.js'
4 | import { testStore, testFocusNodeState } from '@shaperone/testing/models/form.js'
5 | import { sinon } from '@shaperone/testing'
6 | import type { Store } from '@hydrofoil/shaperone-core/state'
7 | import { validate } from '@hydrofoil/shaperone-core/models/forms/effects/validate.js'
8 |
9 | describe('@hydrofoil/shaperone-core/models/forms/effects/validate', () => {
10 | let store: Store
11 |
12 | beforeEach(() => {
13 | store = testStore()
14 | })
15 |
16 | it('calls validator and dispatches report update', async () => {
17 | // given
18 | const focusNode = $rdf.clownface().namedNode('foo')
19 | store.getState().form.focusNodes = testFocusNodeState(focusNode)
20 | const validationResult = {
21 | term: $rdf.blankNode(),
22 | dataset: $rdf.dataset(),
23 | }
24 | store.getState().validation.validator = sinon.stub().resolves(validationResult)
25 |
26 | // when
27 | await validate(store)()
28 |
29 | // then
30 | const dispatch = store.getDispatch()
31 | expect(store.getState().validation.validator).to.have.been.called
32 | expect(dispatch.form.validationReport).to.have.been.called
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/packages/core-tests/models/forms/reducers/pushFocusNode.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import $rdf from '@shaperone/testing/env.js'
4 | import { sinon } from '@shaperone/testing'
5 | import { testStore } from '@shaperone/testing/models/form.js'
6 | import { pushFocusNode } from '@hydrofoil/shaperone-core/models/forms/effects/pushFocusNode.js'
7 | import type { Store } from '@hydrofoil/shaperone-core/state'
8 | import { propertyShape } from '@shaperone/testing/util.js'
9 |
10 | const ex = $rdf.namespace('http://example.com/')
11 |
12 | describe('core/models/forms/reducers/pushFocusNode', () => {
13 | let store: Store
14 | beforeEach(() => {
15 | store = testStore()
16 | })
17 |
18 | it('dispatches initialization of new node state', () => {
19 | // given
20 | const graph = $rdf.clownface()
21 | const property = propertyShape(graph.namedNode(ex.propertyShape))
22 |
23 | // when
24 | pushFocusNode(store)({
25 | focusNode: graph.node(ex.FocusNode),
26 | property,
27 | })
28 |
29 | // then
30 | expect(store.getDispatch().form.createFocusNodeState).to.have.been.calledWith(sinon.match({
31 | appendToStack: true,
32 | }))
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/packages/core-tests/models/forms/reducers/selectGroup.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import $rdf from '@shaperone/testing/env.js'
4 | import { testFocusNodeState, testFormState as testState } from '@shaperone/testing/models/form.js'
5 | import { selectGroup } from '@hydrofoil/shaperone-core/models/forms/reducers/selectGroup.js'
6 |
7 | const ex = $rdf.namespace('http://example.com/')
8 |
9 | describe('core/models/forms/reducers/selectGroup', () => {
10 | it('sets flag on selected group and unsets others', () => {
11 | // given
12 | const graph = $rdf.clownface()
13 | const focusNode = graph.node(ex.FocusNode)
14 | const state = testState({
15 | focusNodes: {
16 | ...testFocusNodeState(focusNode, {
17 | groups: [{
18 | selected: true,
19 | group: $rdf.rdfine.sh.PropertyGroup(graph.node(ex.Group0)),
20 | order: 0,
21 | }, {
22 | selected: true,
23 | group: $rdf.rdfine.sh.PropertyGroup(graph.node(ex.Group1)),
24 | order: 1,
25 | }],
26 | }),
27 | },
28 | })
29 |
30 | // when
31 | const next = selectGroup(state, { focusNode, group: $rdf.rdfine.sh.PropertyGroup(graph.node(ex.Group1)) })
32 |
33 | // then
34 | const focusNodeState = next.focusNodes[ex.FocusNode.value]
35 | expect(focusNodeState.groups[0].selected).to.be.false
36 | expect(focusNodeState.groups[1].selected).to.be.true
37 | })
38 | })
39 |
--------------------------------------------------------------------------------
/packages/core-tests/models/resources/reducers/setRoot.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import $rdf from '@shaperone/testing/env.js'
4 | import { testStore } from '@shaperone/testing/models/form.js'
5 | import { setRoot } from '@hydrofoil/shaperone-core/models/resources/reducers/setRoot.js'
6 |
7 | const ex = $rdf.namespace('http://example.com/')
8 |
9 | describe('core/models/resources/reducers/setRoot', () => {
10 | it('sets dataset to state', () => {
11 | // given
12 | const dataset = $rdf.dataset()
13 | const graph = $rdf.clownface({ dataset })
14 | const store = testStore()
15 |
16 | // when
17 | const after = setRoot(store.getState().resources, {
18 | rootPointer: graph.node(ex.Foo),
19 | })
20 |
21 | // then
22 | expect(after.graph === graph)
23 | })
24 | })
25 |
--------------------------------------------------------------------------------
/packages/core-tests/models/validation/reducers/setValidator.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'mocha'
2 | import { expect } from 'chai'
3 | import type { ValidatorState } from '@hydrofoil/shaperone-core/models/validation'
4 | import { setValidator } from '@hydrofoil/shaperone-core/models/validation/reducers/setValidator.js'
5 |
6 | describe('@hydrofoil/shaperone-core/models/validation/reducers/setValidator', () => {
7 | it('gets replaced in state', () => {
8 | // given
9 | const before: ValidatorState = {
10 | validator: async () => ({} as any),
11 | }
12 |
13 | // when
14 | const newValidator = async () => ({} as any)
15 | const after = setValidator(before, newValidator)
16 |
17 | // then
18 | expect(after.validator).to.eq(newValidator)
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/packages/core-tests/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@hydrofoil/shaperone-core-tests",
3 | "private": true,
4 | "version": "0.1.1",
5 | "type": "module",
6 | "scripts": {
7 | "test": "mocha \"**/*.ts\" --exclude \"node_modules/**/*.ts\""
8 | },
9 | "devDependencies": {
10 | "@hydrofoil/shaperone-core": "*",
11 | "@rdfine/shacl": "^0.10.5",
12 | "@shaperone/testing": "*",
13 | "@tpluscode/rdf-ns-builders": "^4.3.0",
14 | "@zazuko/env": "^2.1.0",
15 | "chai": "^5",
16 | "mocha": "^11.0.0",
17 | "@types/chai": "^5",
18 | "@types/chai-subset": "^1.3.5",
19 | "@types/mocha": "^10.0.10",
20 | "@types/sinon-chai": "^3.2.4",
21 | "autoesm": "^1.0.5",
22 | "chai-snapshot-matcher": "^2.0.3",
23 | "chai-subset": "^1.6.0",
24 | "chai-quantifiers": "^1.0.18",
25 | "deepmerge": "^4.2.2",
26 | "promise-the-world": "^1.0.1",
27 | "rdf-dataset-ext": "^1.1.0"
28 | },
29 | "mocha": {
30 | "require": [
31 | "./test-setup.ts"
32 | ],
33 | "loader": "ts-node/esm/transpile-only"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/core-tests/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'chai-snapshot-matcher'
2 | import chaiQuantifiers from 'chai-quantifiers'
3 | import * as chai from 'chai'
4 | import sinonChai from 'sinon-chai'
5 | import chaiSubset from 'chai-subset'
6 | import type { RequiredEnvironment } from '@hydrofoil/shaperone-core/env.js'
7 | import { setEnv } from '@hydrofoil/shaperone-core/env.js'
8 | import rdf from '@zazuko/env'
9 |
10 | chai.use(sinonChai)
11 | chai.use(chaiQuantifiers)
12 | chai.use(chaiSubset)
13 |
14 | export const mochaHooks = {
15 | beforeAll() {
16 | setEnv(rdf as unknown as RequiredEnvironment)
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/packages/core/esbuild.config.js:
--------------------------------------------------------------------------------
1 | import p from './package.json' with { type: 'json' }
2 |
3 | export default {
4 | entryPoints: [
5 | '*.ts',
6 | 'lib/**/*.ts',
7 | 'models/**/*.ts',
8 | ],
9 | format: 'esm',
10 | platform: 'browser',
11 | outdir: '.',
12 | bundle: true,
13 | splitting: true,
14 | sourcemap: true,
15 | external: [
16 | 'crypto',
17 | ...Object.keys(p.dependencies),
18 | ],
19 | }
20 |
--------------------------------------------------------------------------------
/packages/core/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @packageDocumentation
3 | * @module @hydrofoil/shaperone-core
4 | */
5 |
6 | import type { BlankNode, NamedNode } from '@rdfjs/types'
7 | import type { GraphPointer } from 'clownface'
8 | import '@rdfine/dash/extensions/sh/PropertyShape'
9 |
10 | /**
11 | * A [focus node](https://www.w3.org/TR/shacl/#focusNodes) is a graph pointer to a Named Node or a Blank Node
12 | */
13 | export type FocusNode = GraphPointer
14 | export type { Component, SingleEditorComponent, MultiEditorComponent, RenderComponent, Lazy } from './models/components/index.js'
15 | export type { Editor, SingleEditor, MultiEditor } from './models/editors/index.js'
16 |
--------------------------------------------------------------------------------
/packages/core/lib/RecursivePartial.ts:
--------------------------------------------------------------------------------
1 | export type RecursivePartial = {
2 | [P in keyof T]?:
3 | T[P] extends (infer U)[] ? RecursivePartial[] :
4 | // eslint-disable-next-line @typescript-eslint/ban-types
5 | T[P] extends object ? RecursivePartial :
6 | T[P];
7 | }
8 |
--------------------------------------------------------------------------------
/packages/core/lib/components.ts:
--------------------------------------------------------------------------------
1 | import type { GraphPointer } from 'clownface'
2 | import { getLocalizedLabel } from '@rdfjs-elements/lit-helpers'
3 | import { rdfs } from '@tpluscode/rdf-ns-builders'
4 | import type { PropertyShape } from '@rdfine/shacl'
5 | import type { SingleEditorComponent } from '../models/components/index.js'
6 | import env from '../env.js'
7 |
8 | export type CoreComponent> = Omit
9 |
10 | export function sort(shape: PropertyShape) {
11 | const orderByList = [...shape.pointer.out(env().ns.sh1.orderBy).list() || []]
12 | const orderByPredicates = orderByList.length ? orderByList.map(i => i.term) : [rdfs.label]
13 |
14 | return (left: GraphPointer, right: GraphPointer) => orderByPredicates.reduce((result, predicate) => {
15 | if (result) {
16 | return result
17 | }
18 |
19 | const leftLabel = getLocalizedLabel(left.out(predicate)) || left.value
20 | const rightLabel = getLocalizedLabel(right.out(predicate)) || right.value
21 | return leftLabel.localeCompare(rightLabel)
22 | }, 0)
23 | }
24 |
--------------------------------------------------------------------------------
/packages/core/lib/components/autoComplete.ts:
--------------------------------------------------------------------------------
1 | import { dash } from '@tpluscode/rdf-ns-builders'
2 | import type { CoreComponent } from '../components.js'
3 | import { sort } from '../components.js'
4 | import * as select from './base/instancesSelect.js'
5 | import type { State, Editor } from './base/instancesSelect.js'
6 |
7 | /**
8 | * Represents the state of an instances select component
9 | */
10 | export interface AutoComplete extends State {
11 | freetextQuery?: string
12 | }
13 |
14 | export interface AutoCompleteEditor extends Editor {
15 | }
16 |
17 | /**
18 | * A base implementation of Instances Select component which sets {@link InstancesSelect.ready} state flag the instances are first loaded.
19 | *
20 | * The instance data will be loaded from the shapes graph
21 | */
22 | export const autoComplete: CoreComponent = {
23 | editor: dash.AutoCompleteEditor,
24 | ...select,
25 | sort,
26 | }
27 |
--------------------------------------------------------------------------------
/packages/core/lib/components/blankNode.ts:
--------------------------------------------------------------------------------
1 | import { dash } from '@tpluscode/rdf-ns-builders/loose'
2 | import type { ComponentInstance, SingleEditorComponent } from '../../models/components/index.js'
3 | import type { CoreComponent } from '../components.js'
4 |
5 | /**
6 | * Instance state of blank node component
7 | */
8 | export interface BlankNode extends ComponentInstance {
9 | }
10 |
11 | export interface BlankNodeEditor extends SingleEditorComponent {
12 | }
13 |
14 | /**
15 | * Extend to implement [DASH blank node editor](http://datashapes.org/forms.html#BlankNodeEditor)
16 | */
17 | export const blankNode: CoreComponent = {
18 | editor: dash.BlankNodeEditor,
19 | }
20 |
--------------------------------------------------------------------------------
/packages/core/lib/components/booleanSelect.ts:
--------------------------------------------------------------------------------
1 | import { dash } from '@tpluscode/rdf-ns-builders'
2 | import type { ComponentInstance, SingleEditorComponent } from '../../models/components/index.js'
3 | import type { CoreComponent } from '../components.js'
4 |
5 | /**
6 | * Instance state of boolean select component
7 | */
8 | export interface BooleanSelect extends ComponentInstance {
9 | }
10 |
11 | export interface BooleanSelectEditor extends SingleEditorComponent {
12 | }
13 |
14 | /**
15 | * Extend to implement [DASH boolean select editor](http://datashapes.org/forms.html#BooleanSelectEditor)
16 | */
17 | export const booleanSelect: CoreComponent = {
18 | editor: dash.BooleanSelectEditor,
19 | }
20 |
--------------------------------------------------------------------------------
/packages/core/lib/components/datePicker.ts:
--------------------------------------------------------------------------------
1 | import { dash } from '@tpluscode/rdf-ns-builders'
2 | import type { ComponentInstance, SingleEditorComponent } from '../../models/components/index.js'
3 | import type { CoreComponent } from '../components.js'
4 |
5 | /**
6 | * Instance state of date picker component
7 | */
8 | export interface DatePicker extends ComponentInstance {
9 | }
10 |
11 | export interface DatePickerEditor extends SingleEditorComponent {
12 | }
13 |
14 | /**
15 | * Extend to implement [DASH date picker editor](http://datashapes.org/forms.html#DatePickerEditor)
16 | */
17 | export const datePicker: CoreComponent = {
18 | editor: dash.DatePickerEditor,
19 | }
20 |
--------------------------------------------------------------------------------
/packages/core/lib/components/dateTimePicker.ts:
--------------------------------------------------------------------------------
1 | import { dash } from '@tpluscode/rdf-ns-builders'
2 | import type { ComponentInstance, SingleEditorComponent } from '../../models/components/index.js'
3 | import type { CoreComponent } from '../components.js'
4 |
5 | /**
6 | * Instance state of date-time picker component
7 | */
8 | export interface DateTimePicker extends ComponentInstance {
9 | }
10 |
11 | export interface DateTimePickerEditor extends SingleEditorComponent {
12 | }
13 |
14 | /**
15 | * Extend to implement [DASH date-time picker editor](http://datashapes.org/forms.html#DateTimePickerEditor)
16 | */
17 | export const dateTimePicker: CoreComponent = {
18 | editor: dash.DateTimePickerEditor,
19 | }
20 |
--------------------------------------------------------------------------------
/packages/core/lib/components/details.ts:
--------------------------------------------------------------------------------
1 | import { dash } from '@tpluscode/rdf-ns-builders'
2 | import type { ComponentInstance, SingleEditorComponent } from '../../models/components/index.js'
3 | import type { CoreComponent } from '../components.js'
4 |
5 | /**
6 | * Instance state of details component
7 | */
8 | export interface Details extends ComponentInstance {
9 | }
10 |
11 | export interface DetailsEditor extends SingleEditorComponent {
12 | }
13 |
14 | /**
15 | * Extend to implement [DASH details editor](http://datashapes.org/forms.html#DetailsEditor)
16 | */
17 | export const details: CoreComponent = {
18 | editor: dash.DetailsEditor,
19 | }
20 |
--------------------------------------------------------------------------------
/packages/core/lib/components/instancesSelect.ts:
--------------------------------------------------------------------------------
1 | import { dash } from '@tpluscode/rdf-ns-builders'
2 | import type { CoreComponent } from '../components.js'
3 | import { sort } from '../components.js'
4 | import * as select from './base/instancesSelect.js'
5 | import type { State, Editor } from './base/instancesSelect.js'
6 |
7 | /**
8 | * Represents the state of an instances select component
9 | */
10 | export interface InstancesSelect extends State {
11 | }
12 |
13 | export interface InstancesSelectEditor extends Editor {
14 | }
15 |
16 | /**
17 | * A base implementation of Instances Select component which sets {@link InstancesSelect.ready} state flag the instances are first loaded.
18 | *
19 | * The instance data will be loaded from the shapes graph
20 | */
21 | export const instancesSelect: CoreComponent = {
22 | editor: dash.InstancesSelectEditor,
23 | ...select,
24 | sort,
25 | }
26 |
--------------------------------------------------------------------------------
/packages/core/lib/components/richText.ts:
--------------------------------------------------------------------------------
1 | import { dash } from '@tpluscode/rdf-ns-builders'
2 | import type { ComponentInstance, SingleEditorComponent } from '../../models/components/index.js'
3 | import type { CoreComponent } from '../components.js'
4 |
5 | /**
6 | * Instance state of rich text component
7 | */
8 | export interface RichText extends ComponentInstance {
9 | }
10 |
11 | export interface RichTextEditor extends SingleEditorComponent {
12 | }
13 |
14 | /**
15 | * Extend to implement [DASH rich text editor](http://datashapes.org/forms.html#RichTextEditor)
16 | */
17 | export const richText: CoreComponent = {
18 | editor: dash.RichTextEditor,
19 | }
20 |
--------------------------------------------------------------------------------
/packages/core/lib/components/textArea.ts:
--------------------------------------------------------------------------------
1 | import { dash } from '@tpluscode/rdf-ns-builders'
2 | import type { ComponentInstance, SingleEditorComponent } from '../../models/components/index.js'
3 | import type { CoreComponent } from '../components.js'
4 |
5 | /**
6 | * Instance state of text area component
7 | */
8 | export interface TextArea extends ComponentInstance {
9 | }
10 |
11 | export interface TextAreaEditor extends SingleEditorComponent