-
Notifications
You must be signed in to change notification settings - Fork 60
Configurable list styles #149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| { | ||
| "name": "@editorjs/list", | ||
| "version": "2.0.9", | ||
| "version": "2.1.0", | ||
| "keywords": [ | ||
| "codex editor", | ||
| "list", | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,6 +22,7 @@ import stripNumbers from './utils/stripNumbers'; | |||||||||||||||||||||
| import normalizeData from './utils/normalizeData'; | ||||||||||||||||||||||
| import type { PasteEvent } from './types'; | ||||||||||||||||||||||
| import type { OrderedListItemMeta } from './types/ItemMeta'; | ||||||||||||||||||||||
| import validateConfig from './utils/validateConfig'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Constructor Params for Editorjs List Tool, use to pass initial data and settings | ||||||||||||||||||||||
|
|
@@ -52,7 +53,7 @@ export default class EditorjsList { | |||||||||||||||||||||
| * title - title to show in toolbox | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| public static get toolbox(): ToolboxConfig { | ||||||||||||||||||||||
| return [ | ||||||||||||||||||||||
| const defaultToolbox = [ | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| icon: IconListBulleted, | ||||||||||||||||||||||
| title: 'Unordered List', | ||||||||||||||||||||||
|
|
@@ -75,6 +76,12 @@ export default class EditorjsList { | |||||||||||||||||||||
| }, | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| ]; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return EditorjsList.styles | ||||||||||||||||||||||
| ? defaultToolbox.filter( | ||||||||||||||||||||||
| tool => EditorjsList.styles?.includes(tool.data.style as ListDataStyle) | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| : defaultToolbox; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
|
|
@@ -151,6 +158,11 @@ export default class EditorjsList { | |||||||||||||||||||||
| this.listElement = newListElement; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Styles allowed to be used in list | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| private static styles?: ListDataStyle[]; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * The Editor.js API | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
|
|
@@ -210,11 +222,21 @@ export default class EditorjsList { | |||||||||||||||||||||
| this.config = config; | ||||||||||||||||||||||
| this.block = block; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Validate user configuration | ||||||||||||||||||||||
|
||||||||||||||||||||||
| * Validate user configuration | |
| * Validate user configuration | |
| * | |
| * NOTE: Validation occurs during instance construction, after EditorJS tool registration. | |
| * This means: | |
| * 1. Invalid configurations are only detected at runtime when an editor instance is created. | |
| * 2. The error doesn't prevent tool registration, potentially causing confusing behavior. | |
| * 3. Users won't discover configuration errors until they interact with the editor. | |
| * | |
| * This is a limitation of the EditorJS tool API, as configuration is only available at this point. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that setting to a static getter is a good idea. If you have several instances of Editor.js on the page with different configs, they can override each other.
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a static property to store instance-level configuration creates issues in environments where multiple EditorjsList instances exist simultaneously. Each instance will overwrite the static 'styles' property with its own config, causing all instances to use the most recently instantiated configuration. This is particularly problematic when different editor instances need different list style configurations.
Consider making this an instance property instead, or use the instance's config directly where needed.
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary semicolon after the return statement. This is inconsistent with the JavaScript style used in the rest of the file.
| }; | |
| } |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The filtering logic relies on comparing translated and lowercased tune titles ('Unordered', 'Ordered', 'Checklist') with ListDataStyle values ('unordered', 'ordered', 'checklist'). This approach is fragile because:
- It depends on the i18n translation returning specific English values
- If translations are localized to other languages, toLowerCase() won't produce the expected values
- The comparison will fail for non-English locales
Instead, filter by comparing tune.data.style or store the style value directly in each tune definition, similar to how it's done in the toolbox getter.
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -99,4 +99,8 @@ export interface ListConfig { | |||||||||||
| * @default undefined // All counter types are available when not specified | ||||||||||||
| */ | ||||||||||||
| counterTypes?: OlCounterType[]; | ||||||||||||
| /** | ||||||||||||
| * Styles allowed to be used in list | ||||||||||||
|
||||||||||||
| * Styles allowed to be used in list | |
| * Specifies which list styles are available for selection in both the toolbox and settings menu. | |
| * If not specified, all styles ('ordered', 'unordered', 'checklist') are available by default. | |
| * @example ['ordered', 'unordered'] // Only these two styles will be available for selection | |
| * @default undefined // All styles are available when not specified |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||||||||||||||||||||||||||||||||
| import type { ListConfig } from '../types/ListParams'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Method that will validate config object | ||||||||||||||||||||||||||||||||||||
| * @param config - user configuration object for the List tool | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| export default function validateConfig(config: ListConfig | undefined): void { | ||||||||||||||||||||||||||||||||||||
| if (config === undefined) { | ||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const { styles, defaultStyle } = config; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (defaultStyle !== undefined && Array.isArray(styles) && styles.length > 0 && !styles.includes(defaultStyle)) { | ||||||||||||||||||||||||||||||||||||
| throw new Error(`Invalid config: defaultStyle '${defaultStyle}' must be included in 'styles' ${JSON.stringify(styles)}.`); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+14
to
+16
|
||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+14
to
+17
|
||||||||||||||||||||||||||||||||||||
| if (defaultStyle !== undefined && Array.isArray(styles) && styles.length > 0 && !styles.includes(defaultStyle)) { | |
| throw new Error(`Invalid config: defaultStyle '${defaultStyle}' must be included in 'styles' ${JSON.stringify(styles)}.`); | |
| } | |
| } | |
| // List of allowed ListDataStyle values | |
| const allowedStyles = ['unordered', 'ordered', 'checklist']; | |
| if (defaultStyle !== undefined && Array.isArray(styles) && styles.length > 0 && !styles.includes(defaultStyle)) { | |
| throw new Error(`Invalid config: defaultStyle '${defaultStyle}' must be included in 'styles' ${JSON.stringify(styles)}.`); | |
| } | |
| // If defaultStyle is set but styles is not, validate defaultStyle is a valid value | |
| if (defaultStyle !== undefined && (!Array.isArray(styles) || styles.length === 0)) { | |
| if (!allowedStyles.includes(defaultStyle)) { | |
| throw new Error(`Invalid config: defaultStyle '${defaultStyle}' must be one of the allowed styles: ${JSON.stringify(allowedStyles)}.`); | |
| } | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'toolbox' getter is static and accesses the static 'styles' property, but there's a timing issue: this getter may be called before any instance is created (during tool registration), when 'EditorjsList.styles' is still undefined. This means the toolbox will always show all styles initially, even if a styles configuration is later provided.
Consider accepting configuration at tool registration time, or ensure the toolbox getter has access to the configuration without relying on instance creation.