ckeditor5-elements-core 49.0.0-alpha.0 → 49.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +2 -2
  2. package/dist/elementOrPositionButtonView.d.ts +10 -4
  3. package/dist/elementOrPositionButtonView.d.ts.map +1 -1
  4. package/dist/elementOrPositionButtonView.js +8 -2
  5. package/dist/elementOrPositionButtonView.js.map +1 -1
  6. package/dist/index.d.ts +6 -9
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +6 -10
  9. package/dist/index.js.map +1 -1
  10. package/dist/matching.d.ts +24 -0
  11. package/dist/matching.d.ts.map +1 -0
  12. package/dist/matching.js +27 -0
  13. package/dist/matching.js.map +1 -0
  14. package/dist/singleCopyGuard.js +2 -2
  15. package/dist/utils.d.ts +17 -1
  16. package/dist/utils.d.ts.map +1 -1
  17. package/dist/utils.js +38 -0
  18. package/dist/utils.js.map +1 -1
  19. package/package.json +2 -4
  20. package/src/elementOrPositionButtonView.ts +22 -6
  21. package/src/index.ts +8 -18
  22. package/src/matching.ts +45 -0
  23. package/src/singleCopyGuard.ts +2 -2
  24. package/src/utils.ts +49 -1
  25. package/dist/augmentation.d.ts +0 -7
  26. package/dist/augmentation.d.ts.map +0 -1
  27. package/dist/augmentation.js +0 -2
  28. package/dist/augmentation.js.map +0 -1
  29. package/dist/elementMenu.d.ts +0 -93
  30. package/dist/elementMenu.d.ts.map +0 -1
  31. package/dist/elementMenu.js +0 -160
  32. package/dist/elementMenu.js.map +0 -1
  33. package/dist/elementMenuItemButtonView.d.ts +0 -24
  34. package/dist/elementMenuItemButtonView.d.ts.map +0 -1
  35. package/dist/elementMenuItemButtonView.js +0 -29
  36. package/dist/elementMenuItemButtonView.js.map +0 -1
  37. package/dist/elementMenuView.d.ts +0 -35
  38. package/dist/elementMenuView.d.ts.map +0 -1
  39. package/dist/elementMenuView.js +0 -118
  40. package/dist/elementMenuView.js.map +0 -1
  41. package/dist/menuSeparatorView.d.ts +0 -8
  42. package/dist/menuSeparatorView.d.ts.map +0 -1
  43. package/dist/menuSeparatorView.js +0 -16
  44. package/dist/menuSeparatorView.js.map +0 -1
  45. package/dist/submenuView.d.ts +0 -68
  46. package/dist/submenuView.d.ts.map +0 -1
  47. package/dist/submenuView.js +0 -159
  48. package/dist/submenuView.js.map +0 -1
  49. package/src/augmentation.ts +0 -7
  50. package/src/elementMenu.ts +0 -272
  51. package/src/elementMenuView.ts +0 -164
  52. package/theme/elementMenu.css +0 -14
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # ckeditor5-elements-core
2
2
 
3
3
  The base of CKEditor 5 Elements: commands that act on a position or an element (`ElementOrPositionCommand`,
4
- `ElementCommand`), their buttons (`ElementOrPositionButtonView`, `ElementButtonView`), and the `ElementMenu` plugin, which
5
- shows the menus of the context menu, the breadcrumbs and quick insert.
4
+ `ElementCommand`), and their buttons (`ElementOrPositionButtonView`, `ElementButtonView`). The menus of commands are in
5
+ `ckeditor5-elements-menu`.
6
6
 
7
7
  ## Installation
8
8
 
@@ -10,11 +10,17 @@ export interface ElementOrPositionButtonViewOptions {
10
10
  */
11
11
  withText?: boolean;
12
12
  /**
13
- * The position or element the button acts on. Without it, the button follows the command state for the selection
14
- * and executes the command for the selection.
13
+ * The position or element the button acts on, or a function that returns it, e.g. the element of a floating
14
+ * toolbar, which changes as the selection moves. The function is called whenever the button updates or is clicked;
15
+ * when it returns `null`, the button is disabled. Without a target, the button follows the command state for the
16
+ * selection and executes the command for the selection.
15
17
  */
16
- target?: ElementOrPosition;
18
+ target?: ElementOrPositionTarget;
17
19
  }
20
+ /**
21
+ * The target of a button: a position or an element, or a function that returns one (or `null`).
22
+ */
23
+ export type ElementOrPositionTarget = ElementOrPosition | (() => ElementOrPosition | null);
18
24
  /**
19
25
  * A button that reflects an {@link ElementOrPositionCommand} and executes it when clicked.
20
26
  *
@@ -34,7 +40,7 @@ export default class ElementOrPositionButtonView extends ButtonView {
34
40
  /**
35
41
  * The position or element the button acts on, if any.
36
42
  */
37
- readonly target: ElementOrPosition | undefined;
43
+ readonly target: ElementOrPositionTarget | undefined;
38
44
  constructor(locale: Locale | undefined, command: ElementOrPositionCommand, options?: ElementOrPositionButtonViewOptions);
39
45
  }
40
46
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"elementOrPositionButtonView.d.ts","sourceRoot":"","sources":["../src/elementOrPositionButtonView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,KAAK,wBAAwB,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,kCAAkC;IAClD;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,OAAO,2BAA4B,SAAQ,UAAU;IAClE;;OAEG;IACH,SAAgB,OAAO,EAAE,wBAAwB,CAAC;IAElD;;OAEG;IACH,SAAgB,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;gBAGrD,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,wBAAwB,EACjC,OAAO,GAAE,kCAAuC;CASjD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,wBAAwB,EACjC,EAAE,QAAgB,EAAE,MAAM,EAAE,GAAE,kCAAuC,GACnE,IAAI,CAwCN"}
1
+ {"version":3,"file":"elementOrPositionButtonView.d.ts","sourceRoot":"","sources":["../src/elementOrPositionButtonView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,KAAK,wBAAwB,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,kCAAkC;IAClD;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,uBAAuB,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,iBAAiB,GAAG,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,CAAC;AAE3F;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,OAAO,2BAA4B,SAAQ,UAAU;IAClE;;OAEG;IACH,SAAgB,OAAO,EAAE,wBAAwB,CAAC;IAElD;;OAEG;IACH,SAAgB,MAAM,EAAE,uBAAuB,GAAG,SAAS,CAAC;gBAG3D,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,wBAAwB,EACjC,OAAO,GAAE,kCAAuC;CASjD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,wBAAwB,EACjC,EAAE,QAAgB,EAAE,MAAM,EAAE,GAAE,kCAAuC,GACnE,IAAI,CAiDN"}
@@ -41,9 +41,11 @@ export function bindButtonToCommand(button, command, { withText = false, target
41
41
  else {
42
42
  button.bind('tooltip').to(command, 'tooltip');
43
43
  }
44
+ const getTarget = typeof target === 'function' ? target : () => target ?? null;
44
45
  if (target) {
45
46
  const updateState = () => {
46
- const { isEnabled, isOn } = command.getState(target);
47
+ const current = getTarget();
48
+ const { isEnabled, isOn } = current ? command.getState(current) : { isEnabled: false, isOn: false };
47
49
  button.isEnabled = isEnabled;
48
50
  button.isOn = isOn;
49
51
  };
@@ -58,7 +60,11 @@ export function bindButtonToCommand(button, command, { withText = false, target
58
60
  button.on('execute', () => {
59
61
  // Focus first: focusing reads the DOM selection, which would override a selection set by the command.
60
62
  editor.editing.view.focus();
61
- const result = target ? command.executeFor(target) : command.execute();
63
+ const current = getTarget();
64
+ if (target && !current) {
65
+ return;
66
+ }
67
+ const result = current ? command.executeFor(current) : command.execute();
62
68
  if (result instanceof Promise) {
63
69
  result.catch((error) => {
64
70
  reportError(command, error);
@@ -1 +1 @@
1
- {"version":3,"file":"elementOrPositionButtonView.js","sourceRoot":"","sources":["../src/elementOrPositionButtonView.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"elementOrPositionButtonView.js","sourceRoot":"","sources":["../src/elementOrPositionButtonView.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,18 +1,15 @@
1
1
  /**
2
- * The base of the element commands, their buttons and menus: `ElementOrPositionCommand` (a command for a position or
3
- * an element, with its title, icon and state for any target), `ElementCommand` (toggles an element), their button
4
- * views, and `ElementMenu` (menus of commands, submenus and separators, used by the context menu, the breadcrumbs and
5
- * quick insert).
2
+ * The base of the element commands and their buttons: `ElementOrPositionCommand` (a command for a position or an
3
+ * element, with its title, icon and state for any target), `ElementCommand` (toggles an element), and their button
4
+ * views. The menus of commands are in `ckeditor5-elements-menu`.
6
5
  *
7
6
  * @module ckeditor5-elements-core
8
7
  */
9
8
  import './singleCopyGuard.js';
10
- export { default as ElementMenu, type ElementMenuConfig, type ElementMenuItem, type ElementMenuShowOptions, type ElementSubmenu } from './elementMenu.js';
11
9
  export { default as ElementOrPositionCommand, type ElementOrPosition } from './elementOrPositionCommand.js';
12
10
  export { default as ElementCommand } from './elementCommand.js';
13
- export { default as ElementOrPositionButtonView, type ElementOrPositionButtonViewOptions } from './elementOrPositionButtonView.js';
11
+ export { default as ElementOrPositionButtonView, bindButtonToCommand, type ElementOrPositionButtonViewOptions, type ElementOrPositionTarget } from './elementOrPositionButtonView.js';
14
12
  export { default as ElementButtonView } from './elementButtonView.js';
15
- export { default as ElementMenuView } from './elementMenuView.js';
16
- export { isInDocument } from './utils.js';
17
- import './augmentation.js';
13
+ export { getElementAtSelection, isInDocument, isStandalone, selectNewElement } from './utils.js';
14
+ export { findPlugin, getElementsAround, matchesElement, type ElementMatcher } from './matching.js';
18
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,sBAAsB,CAAC;AAG9B,OAAO,EACN,OAAO,IAAI,WAAW,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAC5G,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGhE,OAAO,EACN,OAAO,IAAI,2BAA2B,EACtC,KAAK,kCAAkC,EACvC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGlE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,sBAAsB,CAAC;AAG9B,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAC5G,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGhE,OAAO,EACN,OAAO,IAAI,2BAA2B,EACtC,mBAAmB,EACnB,KAAK,kCAAkC,EACvC,KAAK,uBAAuB,EAC5B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGtE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACjG,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,22 +1,18 @@
1
1
  /**
2
- * The base of the element commands, their buttons and menus: `ElementOrPositionCommand` (a command for a position or
3
- * an element, with its title, icon and state for any target), `ElementCommand` (toggles an element), their button
4
- * views, and `ElementMenu` (menus of commands, submenus and separators, used by the context menu, the breadcrumbs and
5
- * quick insert).
2
+ * The base of the element commands and their buttons: `ElementOrPositionCommand` (a command for a position or an
3
+ * element, with its title, icon and state for any target), `ElementCommand` (toggles an element), and their button
4
+ * views. The menus of commands are in `ckeditor5-elements-menu`.
6
5
  *
7
6
  * @module ckeditor5-elements-core
8
7
  */
9
8
  import './singleCopyGuard.js';
10
- // Plugins.
11
- export { default as ElementMenu } from './elementMenu.js';
12
9
  // Commands.
13
10
  export { default as ElementOrPositionCommand } from './elementOrPositionCommand.js';
14
11
  export { default as ElementCommand } from './elementCommand.js';
15
12
  // Views.
16
- export { default as ElementOrPositionButtonView } from './elementOrPositionButtonView.js';
13
+ export { default as ElementOrPositionButtonView, bindButtonToCommand } from './elementOrPositionButtonView.js';
17
14
  export { default as ElementButtonView } from './elementButtonView.js';
18
- export { default as ElementMenuView } from './elementMenuView.js';
19
15
  // Utils.
20
- export { isInDocument } from './utils.js';
21
- import './augmentation.js';
16
+ export { getElementAtSelection, isInDocument, isStandalone, selectNewElement } from './utils.js';
17
+ export { findPlugin, getElementsAround, matchesElement } from './matching.js';
22
18
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,sBAAsB,CAAC;AAE9B,WAAW;AACX,OAAO,EACN,OAAO,IAAI,WAAW,EAKtB,MAAM,kBAAkB,CAAC;AAE1B,YAAY;AACZ,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAA0B,MAAM,+BAA+B,CAAC;AAC5G,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEhE,SAAS;AACT,OAAO,EACN,OAAO,IAAI,2BAA2B,EAEtC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAElE,SAAS;AACT,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,sBAAsB,CAAC;AAE9B,YAAY;AACZ,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAA0B,MAAM,+BAA+B,CAAC;AAC5G,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEhE,SAAS;AACT,OAAO,EACN,OAAO,IAAI,2BAA2B,EACtC,mBAAmB,EAGnB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,SAAS;AACT,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACjG,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAuB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { Editor, Mapper, ModelElement, ViewElement } from 'ckeditor5';
2
+ /**
3
+ * Matches model elements: the name of a model element (e.g. `'imageBlock'`), or a function that returns whether it is for
4
+ * the given element (e.g. `element => element.hasAttribute('linkHref')`).
5
+ */
6
+ export type ElementMatcher = string | ((element: ModelElement) => boolean);
7
+ /**
8
+ * Returns whether the matcher is for the element.
9
+ */
10
+ export declare function matchesElement(matcher: ElementMatcher, element: ModelElement): boolean;
11
+ /**
12
+ * Returns the model elements of a view element and its ancestors, the innermost first, with their view elements. The view
13
+ * elements that are not converted from model elements, and roots, are skipped.
14
+ */
15
+ export declare function getElementsAround(mapper: Mapper, viewElement: ViewElement): Generator<{
16
+ element: ModelElement;
17
+ viewElement: ViewElement;
18
+ }>;
19
+ /**
20
+ * Returns the plugin of the name when it is loaded and fits the guard, `null` otherwise. The features use it to work with
21
+ * another feature without depending on it: they look the plugin up by name, and check it against the interface they use.
22
+ */
23
+ export declare function findPlugin<T extends object>(editor: Editor, name: string, isPlugin: (plugin: object) => plugin is T): T | null;
24
+ //# sourceMappingURL=matching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matching.d.ts","sourceRoot":"","sources":["../src/matching.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE3E;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC;AAE3E;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAEtF;AAED;;;GAGG;AACH,wBAAiB,iBAAiB,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,WAAW,GACtB,SAAS,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,WAAW,CAAA;CAAE,CAAC,CAQhE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAC1C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,IAAI,CAAC,GACvC,CAAC,GAAG,IAAI,CAIV"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Returns whether the matcher is for the element.
3
+ */
4
+ export function matchesElement(matcher, element) {
5
+ return typeof matcher === 'string' ? element.name === matcher : matcher(element);
6
+ }
7
+ /**
8
+ * Returns the model elements of a view element and its ancestors, the innermost first, with their view elements. The view
9
+ * elements that are not converted from model elements, and roots, are skipped.
10
+ */
11
+ export function* getElementsAround(mapper, viewElement) {
12
+ for (let node = viewElement; node; node = node.parent?.is('element') ? node.parent : null) {
13
+ const element = mapper.toModelElement(node);
14
+ if (element && !element.is('rootElement')) {
15
+ yield { element, viewElement: node };
16
+ }
17
+ }
18
+ }
19
+ /**
20
+ * Returns the plugin of the name when it is loaded and fits the guard, `null` otherwise. The features use it to work with
21
+ * another feature without depending on it: they look the plugin up by name, and check it against the interface they use.
22
+ */
23
+ export function findPlugin(editor, name, isPlugin) {
24
+ const plugin = editor.plugins.has(name) ? editor.plugins.get(name) : null;
25
+ return typeof plugin === 'object' && plugin !== null && isPlugin(plugin) ? plugin : null;
26
+ }
27
+ //# sourceMappingURL=matching.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matching.js","sourceRoot":"","sources":["../src/matching.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,OAAuB,EAAE,OAAqB;IAC5E,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,MAAM,SAAS,CAAC,CAAC,iBAAiB,CACjC,MAAc,EACd,WAAwB;IAExB,KAAK,IAAI,IAAI,GAAuB,WAAW,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/G,MAAM,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAE5C,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;YAC3C,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QACtC,CAAC;IACF,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CACzB,MAAc,EACd,IAAY,EACZ,QAAyC;IAEzC,MAAM,MAAM,GAAY,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEnF,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1F,CAAC"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Fails when this package is loaded twice, e.g. because two versions of it are installed: `ckeditor5-elements` of one
3
- * version, and a feature package of another. Two copies would register two different `ElementMenu` plugins (which
4
- * CKEditor refuses), and the commands of one copy would not be menu items for the other.
3
+ * version, and a feature package of another. Two copies would have two different `ElementOrPositionCommand` classes,
4
+ * and the commands of one copy would not be menu items for the menus of the other, which recognize them by class.
5
5
  *
6
6
  * It is the counterpart of CKEditor's `ckeditor-duplicated-modules` error. Like CKEditor's packages, this package
7
7
  * declares no `sideEffects` in its `package.json`, so bundlers keep this module, which exports nothing.
package/dist/utils.d.ts CHANGED
@@ -1,7 +1,23 @@
1
- import type { ModelElement, ModelPosition } from 'ckeditor5';
1
+ import type { ModelDocumentSelection, ModelElement, ModelPosition, ModelSchema, ModelSelection, ModelWriter } from 'ckeditor5';
2
2
  /**
3
3
  * Whether the element (or position) is in the document. Unlike `element.isAttached()`, removed elements (moved to
4
4
  * the graveyard root) do not count, and neither do the elements of detached roots.
5
5
  */
6
6
  export declare function isInDocument(elementOrPosition: ModelElement | ModelPosition): boolean;
7
+ /**
8
+ * Returns the element at the selection: the selected element (e.g. an image), or the element with the cursor, i.e. the
9
+ * parent of the start of the selection. Never a root: `null` when the selection is directly in one.
10
+ */
11
+ export declare function getElementAtSelection(selection: ModelSelection | ModelDocumentSelection): ModelElement | null;
12
+ /**
13
+ * Whether the element can be handled as a whole: copied, moved, duplicated or removed. Parts of a structure that cannot
14
+ * stand alone, such as a table cell, a table row or a caption, cannot: in the schema, they are limit elements that are
15
+ * not objects.
16
+ */
17
+ export declare function isStandalone(schema: ModelSchema, element: ModelElement): boolean;
18
+ /**
19
+ * Puts the selection in a newly inserted element, so that the user can go on: an object is selected; an element that
20
+ * holds text gets the cursor inside it; an element that holds blocks gets an empty paragraph with the cursor.
21
+ */
22
+ export declare function selectNewElement(writer: ModelWriter, element: ModelElement): void;
7
23
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE7D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,iBAAiB,EAAE,YAAY,GAAG,aAAa,GAAG,OAAO,CAIrF"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,cAAc,EACd,WAAW,EACX,MAAM,WAAW,CAAC;AAEnB;;;GAGG;AACH,wBAAgB,YAAY,CAAC,iBAAiB,EAAE,YAAY,GAAG,aAAa,GAAG,OAAO,CAIrF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,cAAc,GAAG,sBAAsB,GAAG,YAAY,GAAG,IAAI,CAI7G;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAEhF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAgBjF"}
package/dist/utils.js CHANGED
@@ -6,4 +6,42 @@ export function isInDocument(elementOrPosition) {
6
6
  const root = elementOrPosition.root;
7
7
  return root.is('rootElement') && root.rootName !== '$graveyard' && root.isAttached();
8
8
  }
9
+ /**
10
+ * Returns the element at the selection: the selected element (e.g. an image), or the element with the cursor, i.e. the
11
+ * parent of the start of the selection. Never a root: `null` when the selection is directly in one.
12
+ */
13
+ export function getElementAtSelection(selection) {
14
+ const element = selection.getSelectedElement() ?? selection.getFirstPosition()?.parent;
15
+ return element?.is('element') && !element.is('rootElement') ? element : null;
16
+ }
17
+ /**
18
+ * Whether the element can be handled as a whole: copied, moved, duplicated or removed. Parts of a structure that cannot
19
+ * stand alone, such as a table cell, a table row or a caption, cannot: in the schema, they are limit elements that are
20
+ * not objects.
21
+ */
22
+ export function isStandalone(schema, element) {
23
+ return !schema.isLimit(element) || schema.isObject(element);
24
+ }
25
+ /**
26
+ * Puts the selection in a newly inserted element, so that the user can go on: an object is selected; an element that
27
+ * holds text gets the cursor inside it; an element that holds blocks gets an empty paragraph with the cursor.
28
+ */
29
+ export function selectNewElement(writer, element) {
30
+ const schema = writer.model.schema;
31
+ if (schema.isObject(element)) {
32
+ writer.setSelection(element, 'on');
33
+ }
34
+ else if (schema.checkChild(element, '$text')) {
35
+ writer.setSelection(element, 0);
36
+ }
37
+ else if (schema.checkChild(element, 'paragraph')) {
38
+ const paragraph = writer.createElement('paragraph');
39
+ writer.append(paragraph, element);
40
+ writer.setSelection(paragraph, 0);
41
+ }
42
+ else {
43
+ // Only objects can be selected as a whole. The selection post-fixer moves it to the closest valid place.
44
+ writer.setSelection(element, 'after');
45
+ }
46
+ }
9
47
  //# sourceMappingURL=utils.js.map
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,iBAA+C;IAC3E,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC;IAEpC,OAAO,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,QAAQ,KAAK,YAAY,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;AACtF,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,iBAA+C;IAC3E,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC;IAEpC,OAAO,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,QAAQ,KAAK,YAAY,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;AACtF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAkD;IACvF,MAAM,OAAO,GAAG,SAAS,CAAC,kBAAkB,EAAE,IAAI,SAAS,CAAC,gBAAgB,EAAE,EAAE,MAAM,CAAC;IAEvF,OAAO,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAmB,EAAE,OAAqB;IACtE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAmB,EAAE,OAAqB;IAC1E,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;IAEnC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;SAAM,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;QAChD,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACjC,CAAC;SAAM,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC;QACpD,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;QAEpD,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAClC,MAAM,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACnC,CAAC;SAAM,CAAC;QACP,yGAAyG;QACzG,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ckeditor5-elements-core",
3
- "version": "49.0.0-alpha.0",
3
+ "version": "49.0.0-alpha.4",
4
4
  "description": "The base of the element commands, buttons and menus for CKEditor 5: ElementOrPositionCommand, ElementCommand, their button views, and ElementMenu.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -28,17 +28,15 @@
28
28
  "types": "./dist/index.d.ts",
29
29
  "import": "./dist/index.js"
30
30
  },
31
- "./index.css": "./theme/elementMenu.css",
32
31
  "./package.json": "./package.json"
33
32
  },
34
33
  "files": [
35
34
  "dist",
36
35
  "src",
37
- "theme",
38
36
  "README.md"
39
37
  ],
40
38
  "peerDependencies": {
41
- "ckeditor5": ">=49.0.0"
39
+ "ckeditor5": "^49.0.0"
42
40
  },
43
41
  "scripts": {
44
42
  "build": "tsc --build"
@@ -12,12 +12,19 @@ export interface ElementOrPositionButtonViewOptions {
12
12
  withText?: boolean;
13
13
 
14
14
  /**
15
- * The position or element the button acts on. Without it, the button follows the command state for the selection
16
- * and executes the command for the selection.
15
+ * The position or element the button acts on, or a function that returns it, e.g. the element of a floating
16
+ * toolbar, which changes as the selection moves. The function is called whenever the button updates or is clicked;
17
+ * when it returns `null`, the button is disabled. Without a target, the button follows the command state for the
18
+ * selection and executes the command for the selection.
17
19
  */
18
- target?: ElementOrPosition;
20
+ target?: ElementOrPositionTarget;
19
21
  }
20
22
 
23
+ /**
24
+ * The target of a button: a position or an element, or a function that returns one (or `null`).
25
+ */
26
+ export type ElementOrPositionTarget = ElementOrPosition | (() => ElementOrPosition | null);
27
+
21
28
  /**
22
29
  * A button that reflects an {@link ElementOrPositionCommand} and executes it when clicked.
23
30
  *
@@ -38,7 +45,7 @@ export default class ElementOrPositionButtonView extends ButtonView {
38
45
  /**
39
46
  * The position or element the button acts on, if any.
40
47
  */
41
- public readonly target: ElementOrPosition | undefined;
48
+ public readonly target: ElementOrPositionTarget | undefined;
42
49
 
43
50
  constructor(
44
51
  locale: Locale | undefined,
@@ -74,9 +81,12 @@ export function bindButtonToCommand(
74
81
  button.bind('tooltip').to(command, 'tooltip');
75
82
  }
76
83
 
84
+ const getTarget = typeof target === 'function' ? target : (): ElementOrPosition | null => target ?? null;
85
+
77
86
  if (target) {
78
87
  const updateState = (): void => {
79
- const { isEnabled, isOn } = command.getState(target);
88
+ const current = getTarget();
89
+ const { isEnabled, isOn } = current ? command.getState(current) : { isEnabled: false, isOn: false };
80
90
 
81
91
  button.isEnabled = isEnabled;
82
92
  button.isOn = isOn;
@@ -94,7 +104,13 @@ export function bindButtonToCommand(
94
104
  // Focus first: focusing reads the DOM selection, which would override a selection set by the command.
95
105
  editor.editing.view.focus();
96
106
 
97
- const result = target ? command.executeFor(target) : command.execute();
107
+ const current = getTarget();
108
+
109
+ if (target && !current) {
110
+ return;
111
+ }
112
+
113
+ const result = current ? command.executeFor(current) : command.execute();
98
114
 
99
115
  if (result instanceof Promise) {
100
116
  result.catch((error: unknown) => {
package/src/index.ts CHANGED
@@ -1,23 +1,13 @@
1
1
  /**
2
- * The base of the element commands, their buttons and menus: `ElementOrPositionCommand` (a command for a position or
3
- * an element, with its title, icon and state for any target), `ElementCommand` (toggles an element), their button
4
- * views, and `ElementMenu` (menus of commands, submenus and separators, used by the context menu, the breadcrumbs and
5
- * quick insert).
2
+ * The base of the element commands and their buttons: `ElementOrPositionCommand` (a command for a position or an
3
+ * element, with its title, icon and state for any target), `ElementCommand` (toggles an element), and their button
4
+ * views. The menus of commands are in `ckeditor5-elements-menu`.
6
5
  *
7
6
  * @module ckeditor5-elements-core
8
7
  */
9
8
 
10
9
  import './singleCopyGuard.js';
11
10
 
12
- // Plugins.
13
- export {
14
- default as ElementMenu,
15
- type ElementMenuConfig,
16
- type ElementMenuItem,
17
- type ElementMenuShowOptions,
18
- type ElementSubmenu
19
- } from './elementMenu.js';
20
-
21
11
  // Commands.
22
12
  export { default as ElementOrPositionCommand, type ElementOrPosition } from './elementOrPositionCommand.js';
23
13
  export { default as ElementCommand } from './elementCommand.js';
@@ -25,12 +15,12 @@ export { default as ElementCommand } from './elementCommand.js';
25
15
  // Views.
26
16
  export {
27
17
  default as ElementOrPositionButtonView,
28
- type ElementOrPositionButtonViewOptions
18
+ bindButtonToCommand,
19
+ type ElementOrPositionButtonViewOptions,
20
+ type ElementOrPositionTarget
29
21
  } from './elementOrPositionButtonView.js';
30
22
  export { default as ElementButtonView } from './elementButtonView.js';
31
- export { default as ElementMenuView } from './elementMenuView.js';
32
23
 
33
24
  // Utils.
34
- export { isInDocument } from './utils.js';
35
-
36
- import './augmentation.js';
25
+ export { getElementAtSelection, isInDocument, isStandalone, selectNewElement } from './utils.js';
26
+ export { findPlugin, getElementsAround, matchesElement, type ElementMatcher } from './matching.js';
@@ -0,0 +1,45 @@
1
+ import type { Editor, Mapper, ModelElement, ViewElement } from 'ckeditor5';
2
+
3
+ /**
4
+ * Matches model elements: the name of a model element (e.g. `'imageBlock'`), or a function that returns whether it is for
5
+ * the given element (e.g. `element => element.hasAttribute('linkHref')`).
6
+ */
7
+ export type ElementMatcher = string | ((element: ModelElement) => boolean);
8
+
9
+ /**
10
+ * Returns whether the matcher is for the element.
11
+ */
12
+ export function matchesElement(matcher: ElementMatcher, element: ModelElement): boolean {
13
+ return typeof matcher === 'string' ? element.name === matcher : matcher(element);
14
+ }
15
+
16
+ /**
17
+ * Returns the model elements of a view element and its ancestors, the innermost first, with their view elements. The view
18
+ * elements that are not converted from model elements, and roots, are skipped.
19
+ */
20
+ export function* getElementsAround(
21
+ mapper: Mapper,
22
+ viewElement: ViewElement
23
+ ): Generator<{ element: ModelElement; viewElement: ViewElement }> {
24
+ for (let node: ViewElement | null = viewElement; node; node = node.parent?.is('element') ? node.parent : null) {
25
+ const element = mapper.toModelElement(node);
26
+
27
+ if (element && !element.is('rootElement')) {
28
+ yield { element, viewElement: node };
29
+ }
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Returns the plugin of the name when it is loaded and fits the guard, `null` otherwise. The features use it to work with
35
+ * another feature without depending on it: they look the plugin up by name, and check it against the interface they use.
36
+ */
37
+ export function findPlugin<T extends object>(
38
+ editor: Editor,
39
+ name: string,
40
+ isPlugin: (plugin: object) => plugin is T
41
+ ): T | null {
42
+ const plugin: unknown = editor.plugins.has(name) ? editor.plugins.get(name) : null;
43
+
44
+ return typeof plugin === 'object' && plugin !== null && isPlugin(plugin) ? plugin : null;
45
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Fails when this package is loaded twice, e.g. because two versions of it are installed: `ckeditor5-elements` of one
3
- * version, and a feature package of another. Two copies would register two different `ElementMenu` plugins (which
4
- * CKEditor refuses), and the commands of one copy would not be menu items for the other.
3
+ * version, and a feature package of another. Two copies would have two different `ElementOrPositionCommand` classes,
4
+ * and the commands of one copy would not be menu items for the menus of the other, which recognize them by class.
5
5
  *
6
6
  * It is the counterpart of CKEditor's `ckeditor-duplicated-modules` error. Like CKEditor's packages, this package
7
7
  * declares no `sideEffects` in its `package.json`, so bundlers keep this module, which exports nothing.
package/src/utils.ts CHANGED
@@ -1,4 +1,11 @@
1
- import type { ModelElement, ModelPosition } from 'ckeditor5';
1
+ import type {
2
+ ModelDocumentSelection,
3
+ ModelElement,
4
+ ModelPosition,
5
+ ModelSchema,
6
+ ModelSelection,
7
+ ModelWriter
8
+ } from 'ckeditor5';
2
9
 
3
10
  /**
4
11
  * Whether the element (or position) is in the document. Unlike `element.isAttached()`, removed elements (moved to
@@ -9,3 +16,44 @@ export function isInDocument(elementOrPosition: ModelElement | ModelPosition): b
9
16
 
10
17
  return root.is('rootElement') && root.rootName !== '$graveyard' && root.isAttached();
11
18
  }
19
+
20
+ /**
21
+ * Returns the element at the selection: the selected element (e.g. an image), or the element with the cursor, i.e. the
22
+ * parent of the start of the selection. Never a root: `null` when the selection is directly in one.
23
+ */
24
+ export function getElementAtSelection(selection: ModelSelection | ModelDocumentSelection): ModelElement | null {
25
+ const element = selection.getSelectedElement() ?? selection.getFirstPosition()?.parent;
26
+
27
+ return element?.is('element') && !element.is('rootElement') ? element : null;
28
+ }
29
+
30
+ /**
31
+ * Whether the element can be handled as a whole: copied, moved, duplicated or removed. Parts of a structure that cannot
32
+ * stand alone, such as a table cell, a table row or a caption, cannot: in the schema, they are limit elements that are
33
+ * not objects.
34
+ */
35
+ export function isStandalone(schema: ModelSchema, element: ModelElement): boolean {
36
+ return !schema.isLimit(element) || schema.isObject(element);
37
+ }
38
+
39
+ /**
40
+ * Puts the selection in a newly inserted element, so that the user can go on: an object is selected; an element that
41
+ * holds text gets the cursor inside it; an element that holds blocks gets an empty paragraph with the cursor.
42
+ */
43
+ export function selectNewElement(writer: ModelWriter, element: ModelElement): void {
44
+ const schema = writer.model.schema;
45
+
46
+ if (schema.isObject(element)) {
47
+ writer.setSelection(element, 'on');
48
+ } else if (schema.checkChild(element, '$text')) {
49
+ writer.setSelection(element, 0);
50
+ } else if (schema.checkChild(element, 'paragraph')) {
51
+ const paragraph = writer.createElement('paragraph');
52
+
53
+ writer.append(paragraph, element);
54
+ writer.setSelection(paragraph, 0);
55
+ } else {
56
+ // Only objects can be selected as a whole. The selection post-fixer moves it to the closest valid place.
57
+ writer.setSelection(element, 'after');
58
+ }
59
+ }
@@ -1,7 +0,0 @@
1
- import type { ElementMenu } from './index.js';
2
- declare module 'ckeditor5' {
3
- interface PluginsMap {
4
- [ElementMenu.pluginName]: ElementMenu;
5
- }
6
- }
7
- //# sourceMappingURL=augmentation.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"augmentation.d.ts","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,QAAQ,WAAW,CAAC;IAC1B,UAAU,UAAU;QACnB,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC;KACtC;CACD"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=augmentation.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"augmentation.js","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":""}