diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..2d0dffd0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,16 @@ +--- +name: Bug report +about: Something does not work as expected +labels: bug +--- + +**Version**: `@boxraiser/visual-editor` x.y.z, browser and OS + +**What happens** + + +**What you expected** + + +**How to reproduce** (a minimal component declaration and value help a lot) + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..90463649 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,11 @@ +--- +name: Feature request +about: Suggest an improvement +labels: enhancement +--- + +**The problem** + + +**The solution you would like** + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..23dc7b46 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +**What and why** + + +**Checks** + +- [ ] `pnpm check`, `pnpm build` and `pnpm test` pass in `visual-editor/` +- [ ] No change to the public API or to the stored JSON format (or it is described above) +- [ ] Documentation and changelog updated when needed diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 03e9365c..4c4c36d5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,8 +5,7 @@ name: Publish on: push: tags: - - '0.*' - - '1.*' + - '[0-9]+.[0-9]+.[0-9]+*' workflow_dispatch: permissions: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e810a91f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contributing + +Thanks for helping! Issues and pull requests are welcome, in English or French. + +## Setup + +Requirements: Node 24, pnpm 10 (`corepack enable` picks the version from `package.json`) and PHP (the end to end tests use `php -S`). + +```bash +pnpm install +cd visual-editor +pnpm dev # Vite playground + PHP preview server on :8000 +pnpm check # TypeScript +pnpm build # dist/, what is published on npm +pnpm test # vitest, then Cypress (needs a build) +pnpm format # Prettier +``` + +To run Cypress against another port: start `php -S localhost:8765` in `visual-editor/` and run `CYPRESS_BASE_URL=http://localhost:8765 npx cypress run`. + +## Pull requests + +- One topic per pull request, with a short description of the behaviour change. +- Commit messages follow the existing style: `feat:`, `fix:`, `chore:`, `docs:`, `test:`... +- Keep the public API and the stored JSON format stable. `cypress/e2e/compat.cy.js` replays how an existing host uses the library: if it fails, the change breaks existing users and must be discussed first. +- Add or update the documentation in `docs/docs/` and an entry in `docs/docs/changelog.mdx`. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..5e7a149b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-2026 Ciklik and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 00000000..336cf343 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,29 @@ +# Releasing + +Releases are published to npm by GitHub Actions on every `X.Y.Z` tag (`.github/workflows/publish.yml`) with npm trusted publishing (OIDC): no npm token is stored in the repository, and the package gets a provenance attestation. + +## One time setup + +On npmjs.com, in the settings of `@boxraiser/visual-editor`, add a trusted publisher: + +- Provider: GitHub Actions +- Organization or user: `ciklik` +- Repository: `visual-editor` +- Workflow filename: `publish.yml` +- Environment: leave empty + +Fallback, only if trusted publishing is not available: create a granular npm access token with publish rights on the package and store it as the `NPM_TOKEN` repository secret. The workflow uses it when OIDC is not possible. + +## Release a version + +1. Update `version` in `visual-editor/package.json` and add the entry in `docs/docs/changelog.mdx`, then merge to `main`. +2. Tag the merged commit with the bare version and push the tag: + + ```bash + git tag 0.2.1 + git push origin 0.2.1 + ``` + +3. The `Publish` workflow builds `visual-editor/dist` and publishes it. It fails if the tag does not match the package version. + +If a run fails after the tag is pushed, fix the cause and run the `Publish` workflow again from the Actions tab with "Use workflow from" set to the tag (manual runs are only allowed on tags). diff --git a/docs/docs/changelog.mdx b/docs/docs/changelog.mdx index b11e9325..793ac510 100644 --- a/docs/docs/changelog.mdx +++ b/docs/docs/changelog.mdx @@ -4,17 +4,52 @@ sidebar_position: 10 # Changelog -## 0.2 - 2024/02/01 - +## 0.2.1 - 2026/10/09 + +- Fix: the `change` event carries the JSON of the page in `detail` again (removed in 0.2.0), and is emitted for every change made in the editor, including the preview arrows. It is not emitted when the host sets `value` +- Fix: one store per element, the content is kept when the editor is hidden and shown again, and `value` can be read right after the element is inserted +- Fix: the text alignment radios are no longer submitted with the surrounding form (#4) +- Fix: preview messages are only accepted from the preview iframe and from the editor window +- Fix: English and French translations +- Feat: strike, superscript, subscript and inline code in the text toolbar, "New tab" option on links, "No color" in the color palette (#21) +- Change: links created from the toolbar open in the same tab by default (use the "New tab" option) +- Security: Tiptap 3.31 (prosemirror-view XSS on paste, linkifyjs) and dependency updates +- Chore: MIT license, `exports` map in `package.json` (only the entry point, `VisualEditor.js`, `VisualEditor.standalone.js` and `package.json` can be imported), minified standalone build, the playground icons are no longer published +- Chore: publication to npm with trusted publishing (OIDC) and provenance + +## 0.2.0 - 2026/02/12 + +Not published on npm. Upgrading from 0.1.x, mind these behaviour changes: + +- **Breaking**: the `change` event had no `detail` anymore (restored in 0.2.1) +- **Breaking**: the text toolbar offers h1 headings, and the alignment button is available in multiline fields too: it writes an inline `style="text-align: ..."` on paragraphs and headings +- **Breaking**: blocks can be moved with up and down arrows in the preview (they emit `change` since 0.2.1) - Feat: export UI components - Feat: registerButton() method for adding custom button - Feat: Add control in the preview - Feat: New responsive controls - Feat: Add a new `templates` event (cancelable) - Feat: Add a new `components` event (cancelable) -- Chore: Remove `detail` in the change event - Fix: key missing on templates listing +## 0.1.4 - 2025/12/01 + +- Fix: undefined text alignment in the toolbar + +## 0.1.3 - 2025/11/19 + +- Fix: z-index of the text toolbar + +## 0.1.2 - 2025/07/24 + +- Republication of 0.1.1 + +## 0.1.1 - 2025/07/24 + +- Feat: Tiptap 3 +- Fix: no default text alignment added by Tiptap +- Fix: SSR compatibility + ## 0.1.0 - 2023/03/16 - Feat: Add a new `postMessagePreview` option for VisualEditor diff --git a/docs/docs/events.mdx b/docs/docs/events.mdx index a9666f9d..522de584 100644 --- a/docs/docs/events.mdx +++ b/docs/docs/events.mdx @@ -8,7 +8,7 @@ If you need to create custom behaviour you can listen for some events on the edi | **Event** | **Description** | **Cancellable** | **Methods** | |-----------|-----------------|-------------|-------------| -| `change` | Fires when the value of the editor is changed by the user | No | | +| `change` | Fires when the value of the editor is changed by the user (not when the value is set by the host) | No | **detail** : `string`, the JSON of the page (same as `element.value`) | | `templates` | Fires when the user click on the template button | Yes | | | `components` | Fires when the user click on the add component button | Yes | | | `removeitem` | Fires when a bloc is removed | Yes | **detail.confirm** : `() => void`, triggers the intended action (usefull if event was prevented) | diff --git a/visual-editor/README.md b/visual-editor/README.md index a97852db..55366c87 100644 --- a/visual-editor/README.md +++ b/visual-editor/README.md @@ -1,9 +1,18 @@ # Visual Editor -[](https://badge.fury.io/js/@boxraiser%2Fvisual-editor) -[](https://github.com/boxraiser/visual-editor/actions/workflows/test.yml) +[](https://www.npmjs.com/package/@boxraiser/visual-editor) +[](https://github.com/ciklik/visual-editor/actions/workflows/test.yml) +[](https://github.com/ciklik/visual-editor/blob/main/LICENSE) -[Documentation](http://ciklik.github.io/visual-editor/) +[Documentation](https://ciklik.github.io/visual-editor/) + +## Installation + +```bash +npm install @boxraiser/visual-editor react react-dom +``` + +React 18 is a peer dependency. A build with every dependency included is also available as `@boxraiser/visual-editor/VisualEditor.standalone.js`. ## Getting started @@ -15,15 +24,14 @@ Start by instantiating the editor. ```js import { VisualEditor } from '@boxraiser/visual-editor' -import '@boxraiser/visual-editor/style.css' -let editor = new VisualEditor() +const editor = new VisualEditor() ``` Then register your page components using the `registerComponent` method that expect 2 parameters - A unique identifier for the block (string) -- An option object object (see [EditorComponentDefinition](https://github.com/Grafikart/VisualEditor/blob/master/src/types.ts#L24) for more information) +- An option object object (see [EditorComponentDefinition](https://github.com/ciklik/visual-editor/blob/main/visual-editor/src/types.ts) for more information) ```js import { HTMLText, Repeater, Text, Row, Select } from '@boxraiser/visual-editor' @@ -33,16 +41,16 @@ editor.registerComponent('hero', { title: 'Hero', category: 'Banner', fields: [ - new Text('title', {multiline: false}), - new HTMLText('content'), - new Repeater('buttons', { + Text('title', {multiline: false}), + HTMLText('content'), + Repeater('buttons', { title: 'Boutons', addLabel: 'Add a new button', fields: [ - new Row([ - new Text('label', { label: 'Libellé', default: 'Call to action' }), - new Text('url', { label: 'Lien' }), - new Select('type', { + Row([ + Text('label', { label: 'Libellé', default: 'Call to action' }), + Text('url', { label: 'Lien' }), + Select('type', { default: 'primary', label: 'type', options: [ @@ -89,6 +97,18 @@ There are multiple attributes : The custom element will create a hidden field that will be used to store the data, no need for additional JavaScript you will receive the data when the form is submitted. +### Listening to changes + +The element emits a `change` event every time the content is changed by the user. `event.detail` (and `element.value`) contains the JSON of the page. + +```js +document.querySelector('visual-editor').addEventListener('change', (e) => { + console.log(JSON.parse(e.detail)) +}) +``` + +See the [documentation](https://ciklik.github.io/visual-editor/) for the other events, the fields, the templates and the preview. + ### How the data is formatted ? The data use a simple structure (to be usable with any framework or technology) @@ -117,7 +137,7 @@ A real world example { "title": "Album example", "titleAlign": "center", - "content": "
Something short and leading about the collection below—its contents, the creator, etc. Make it short and sweet, but not too short so folks don't simply skip over it entirely.
", + "content": "Something short and leading about the collection below, its contents, the creator, etc. Make it short and sweet, but not too short so folks don't simply skip over it entirely.
", "buttons": [ { "label": "Main call to action", @@ -226,3 +246,7 @@ A real world example } ] ``` + +## License + +[MIT](https://github.com/ciklik/visual-editor/blob/main/LICENSE) diff --git a/visual-editor/package.json b/visual-editor/package.json index f7ff59c1..4532730a 100644 --- a/visual-editor/package.json +++ b/visual-editor/package.json @@ -1,17 +1,37 @@ { "name": "@boxraiser/visual-editor", - "version": "0.2.0", + "version": "0.2.1", + "description": "Block based visual editor, as a custom element", + "license": "MIT", "type": "module", "main": "VisualEditor.js", "types": "./VisualEditor.d.ts", + "exports": { + ".": { + "types": "./VisualEditor.d.ts", + "default": "./VisualEditor.js" + }, + "./VisualEditor.js": "./VisualEditor.js", + "./VisualEditor.standalone.js": "./VisualEditor.standalone.js", + "./package.json": "./package.json" + }, + "sideEffects": false, + "files": [ + "VisualEditor.js", + "VisualEditor.standalone.js", + "**/*.d.ts", + "LICENSE" + ], "repository": { "type": "git", "url": "https://github.com/ciklik/visual-editor" }, + "homepage": "https://ciklik.github.io/visual-editor/", + "bugs": "https://github.com/ciklik/visual-editor/issues", "scripts": { "dev": "concurrently \"vite\" \"php -S 0.0.0.0:8000 -ddisplay_errors=1 -t server/\"", "check": "tsc", - "build": "tsc && vite build && node esbuild.js && npm run declaration && cp package.json dist/package.json && cp README.md dist/README.md", + "build": "tsc && vite build && node esbuild.js && npm run declaration && cp package.json dist/package.json && cp README.md ../LICENSE dist/", "serve": "vite preview", "format": "prettier --config .prettierrc 'src/**/*.{ts,tsx}' --write", "declaration": "tsc --declaration --emitDeclarationOnly --jsx react-jsx --esModuleInterop --target es2020 -p tsconfig.json --noEmit false && tsc-alias", @@ -93,5 +113,8 @@ }, "volta": { "node": "24.21.0" + }, + "engines": { + "node": ">=18" } } diff --git a/visual-editor/vite.config.ts b/visual-editor/vite.config.ts index 410fb709..510203a3 100644 --- a/visual-editor/vite.config.ts +++ b/visual-editor/vite.config.ts @@ -4,8 +4,10 @@ import react from '@vitejs/plugin-react' import analyze from 'rollup-plugin-analyzer' // https://vitejs.dev/config/ -export default defineConfig({ +export default defineConfig(({ command }) => ({ define: { 'process.env.NODE_ENV': '"production"' }, + // The playground icons are not part of the library + publicDir: command === 'build' ? false : 'public', plugins: [ react({ babel: { @@ -33,7 +35,7 @@ export default defineConfig({ }, }, build: { - minify: false, + minify: true, rollupOptions: { plugins: [ analyze({ @@ -50,4 +52,4 @@ export default defineConfig({ fileName: () => 'VisualEditor.standalone.js', }, }, -}) +}))