Skip to content
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

Add the Bookmarks panel to the link UI. #17340

Merged
merged 29 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d714014
Added bookmarks view panel.
pszczesniak Oct 23, 2024
d96fd2c
Show message when no bookmarks available.
pszczesniak Oct 23, 2024
2ec5eae
Style the empty information when no bookmarks available.
pszczesniak Oct 24, 2024
7abf7b8
Removed commented code.
pszczesniak Oct 24, 2024
23109ae
Code refactor.
niegowski Oct 24, 2024
7b3f6cb
Code refactor.
pszczesniak Oct 24, 2024
edd58fd
Fix the widths of input fields.
pszczesniak Oct 25, 2024
cec3747
Added comments to explain how to manage the focus.
pszczesniak Oct 25, 2024
1677b46
Moved bookmark icons to the core package.
pszczesniak Oct 24, 2024
ebf370c
Added bookmark icon to the bookmark list item.
pszczesniak Oct 25, 2024
77ddc30
Added tests to 'getAllBookmarkNames' method.
pszczesniak Oct 25, 2024
b3a8095
Added tests.
pszczesniak Oct 25, 2024
8eef5f6
Added event type.
pszczesniak Oct 28, 2024
f241f43
Change the order of removing views and update the isUIVisible getter.
pszczesniak Oct 28, 2024
f54ba36
Added tests for LinkUI class.
pszczesniak Oct 28, 2024
6eed8f4
Added more tests.
pszczesniak Oct 28, 2024
2dd03ad
Merge branch 'ck/epic/17230-linking-experience' into ck/17230-bookmar…
pszczesniak Oct 28, 2024
ea8ec24
Styled bookmarks panel.
pszczesniak Oct 28, 2024
b0eaeab
Apply suggestions from code review.
pszczesniak Oct 29, 2024
17e1a9c
Merge branch 'refs/heads/ck/epic/17230-linking-experience' into ck/17…
niegowski Oct 29, 2024
da427fa
Code refactor.
niegowski Oct 29, 2024
7b756cb
Code refactor.
niegowski Oct 29, 2024
b42a230
Code refactor.
niegowski Oct 29, 2024
8094374
Code refactor.
niegowski Oct 29, 2024
77aa258
Code refactor.
niegowski Oct 29, 2024
8ab51c9
Code refactor.
niegowski Oct 29, 2024
b603e81
Code refactor.
niegowski Oct 29, 2024
2127878
Added missing context.
niegowski Oct 29, 2024
327a8ca
Fixed tests.
niegowski Oct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/ckeditor5-bookmark/ckeditor5-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{
"type": "Button",
"name": "bookmark",
"iconPath": "theme/icons/bookmark.svg"
"iconPath": "@ckeditor/ckeditor5-core/theme/icons/bookmark.svg"
}
],
"htmlOutput": [
Expand Down
2 changes: 1 addition & 1 deletion packages/ckeditor5-bookmark/docs/features/bookmark.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The bookmarks feature allows for adding and managing the bookmarks anchors attac

## Demo

Use the bookmark toolbar button {@icon @ckeditor/ckeditor5-bookmark/theme/icons/bookmark.svg Add bookmark} in the editor below to see the feature in action. Or use the "Insert" command from the menu bar to add a bookmark. Add a unique name to identify the bookmark (for example, `Rights`).
Use the bookmark toolbar button {@icon @ckeditor/ckeditor5-core/theme/icons/bookmark.svg Add bookmark} in the editor below to see the feature in action. Or use the "Insert" command from the menu bar to add a bookmark. Add a unique name to identify the bookmark (for example, `Rights`).

To use the bookmark as an anchor in the content, add a link {@icon @ckeditor/ckeditor5-link/theme/icons/link.svg Add link} and put the bookmark name as target. In this example it would be `#Rights`. You can change the bookmark's name or remove it by clicking the bookmark icon inside the content. A contextual bookmark toolbar will pop up.

Expand Down
11 changes: 9 additions & 2 deletions packages/ckeditor5-bookmark/src/bookmarkediting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @module bookmark/bookmarkediting
*/

import { type Editor, Plugin } from 'ckeditor5/src/core.js';
import { type Editor, Plugin, icons } from 'ckeditor5/src/core.js';
pszczesniak marked this conversation as resolved.
Show resolved Hide resolved
import { toWidget } from 'ckeditor5/src/widget.js';
import { IconView } from 'ckeditor5/src/ui.js';
import type { EventInfo } from 'ckeditor5/src/utils.js';
Expand All @@ -28,7 +28,7 @@ import UpdateBookmarkCommand from './updatebookmarkcommand.js';

import '../theme/bookmark.css';

import bookmarkIcon from '../theme/icons/bookmark_inline.svg';
const bookmarkIcon = icons.bookmarkInline;
pszczesniak marked this conversation as resolved.
Show resolved Hide resolved

/**
* The bookmark editing plugin.
Expand Down Expand Up @@ -83,6 +83,13 @@ export default class BookmarkEditing extends Plugin {
return null;
}

/**
* Returns all unique bookmark names existing in the content.
*/
public getAllBookmarkNames(): Set<string> {
return new Set( this._bookmarkElements.values() );
}

/**
* Defines the schema for the bookmark feature.
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/ckeditor5-bookmark/src/bookmarkui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @module bookmark/bookmarkui
*/

import { Plugin, type Editor } from 'ckeditor5/src/core.js';
import { Plugin, type Editor, icons } from 'ckeditor5/src/core.js';
pszczesniak marked this conversation as resolved.
Show resolved Hide resolved
import {
ButtonView,
ContextualBalloon,
Expand All @@ -34,7 +34,7 @@ import type InsertBookmarkCommand from './insertbookmarkcommand.js';

import BookmarkEditing from './bookmarkediting.js';

import bookmarkIcon from '../theme/icons/bookmark.svg';
const bookmarkIcon = icons.bookmark;
pszczesniak marked this conversation as resolved.
Show resolved Hide resolved

const VISUAL_SELECTION_MARKER_NAME = 'bookmark-ui';

Expand Down
39 changes: 39 additions & 0 deletions packages/ckeditor5-bookmark/tests/bookmarkediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,45 @@ describe( 'BookmarkEditing', () => {
} );
} );

describe( 'getAllBookmarkNames', () => {
it( 'should return all bookmark names', () => {
const bookmarkEditing = editor.plugins.get( 'BookmarkEditing' );

editor.setData(
'<p>' +
'<a id="foo"></a>' +
'</p>' +
'<p>' +
'<a id="bar"></a>' +
'</p>' +
'<p>' +
'<a id="baz"></a>' +
'</p>'
);

expect( bookmarkEditing.getAllBookmarkNames() ).is.instanceof( Set );
expect( bookmarkEditing.getAllBookmarkNames() ).is.deep.equal( new Set( [ 'foo', 'bar', 'baz' ] ) );
} );

it( 'should return all unique bookmark names', () => {
const bookmarkEditing = editor.plugins.get( 'BookmarkEditing' );

editor.setData(
'<p>' +
'<a id="foo"></a>' +
'</p>' +
'<p>' +
'<a id="bar"></a>' +
'</p>' +
'<p>' +
'<a id="bar"></a>' +
'</p>'
);

expect( bookmarkEditing.getAllBookmarkNames() ).is.deep.equal( new Set( [ 'foo', 'bar' ] ) );
} );
} );

describe( 'clipboard', () => {
let clipboardPlugin, viewDocument;

Expand Down
3 changes: 2 additions & 1 deletion packages/ckeditor5-bookmark/tests/bookmarkui.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';

import { View, ButtonView, ContextualBalloon, MenuBarMenuListItemButtonView } from '@ckeditor/ckeditor5-ui';
import { icons } from '@ckeditor/ckeditor5-core';
import { ClickObserver } from '@ckeditor/ckeditor5-engine';
import { indexOf, isRange, keyCodes } from '@ckeditor/ckeditor5-utils';
import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model.js';
Expand All @@ -22,7 +23,7 @@ import BookmarkActionsView from '../src/ui/bookmarkactionsview.js';
import BookmarkEditing from '../src/bookmarkediting.js';
import BookmarkUI from '../src/bookmarkui.js';

import bookmarkIcon from '../theme/icons/bookmark.svg';
const bookmarkIcon = icons.bookmark;

describe( 'BookmarkUI', () => {
let editor, element, button, balloon, bookmarkUIFeature, formView, actionsView;
Expand Down
8 changes: 7 additions & 1 deletion packages/ckeditor5-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ import table from './../theme/icons/table.svg';

import remove from './../theme/icons/remove.svg';

import bookmark from './../theme/icons/bookmark.svg';
import bookmarkInline from './../theme/icons/bookmark_inline.svg';

export const icons = {
bold,
cancel,
Expand Down Expand Up @@ -202,7 +205,10 @@ export const icons = {

table,

remove
remove,

bookmark,
bookmarkInline
};

import './augmentation.js';
128 changes: 120 additions & 8 deletions packages/ckeditor5-link/src/linkui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @module link/linkui
*/

import { Plugin, type Editor } from 'ckeditor5/src/core.js';
import { Plugin, type Editor, icons } from 'ckeditor5/src/core.js';
pszczesniak marked this conversation as resolved.
Show resolved Hide resolved
import {
ClickObserver,
type ViewAttributeElement,
Expand All @@ -24,10 +24,12 @@ import {
MenuBarMenuListItemButtonView,
type ViewWithCssTransitionDisabler
} from 'ckeditor5/src/ui.js';

import type { PositionOptions } from 'ckeditor5/src/utils.js';
import { isWidget } from 'ckeditor5/src/widget.js';

import LinkFormView, { type LinkFormValidatorCallback } from './ui/linkformview.js';
import LinkBookmarksView from './ui/linkbookmarksview.js';
import LinkAdvancedView from './ui/linkadvancedview.js';
import LinkButtonView from './ui/linkbuttonview.js';
import LinkActionsView from './ui/linkactionsview.js';
Expand All @@ -42,6 +44,7 @@ import {

import linkIcon from '../theme/icons/link.svg';

const bookmarkIcon = icons.bookmark;
pszczesniak marked this conversation as resolved.
Show resolved Hide resolved
const VISUAL_SELECTION_MARKER_NAME = 'link-ui';

/**
Expand All @@ -61,6 +64,11 @@ export default class LinkUI extends Plugin {
*/
public formView: LinkFormView & ViewWithCssTransitionDisabler | null = null;

/**
* The view displaying bookmarks list.
*/
public bookmarksView: LinkBookmarksView | null = null;

/**
* The form view displaying advanced link settings.
*/
Expand Down Expand Up @@ -170,6 +178,10 @@ export default class LinkUI extends Plugin {
if ( this.actionsView ) {
this.actionsView.destroy();
}

if ( this.bookmarksView ) {
this.bookmarksView.destroy();
}
}

/**
Expand Down Expand Up @@ -238,7 +250,9 @@ export default class LinkUI extends Plugin {

const formView = new ( CssTransitionDisablerMixin( LinkFormView ) )( editor.locale, getFormValidators( editor ) );

if ( editor.plugins.has( 'BookmarkEditing' ) ) {
if ( this.editor.plugins.has( 'BookmarkEditing' ) ) {
this.bookmarksView = this._createBookmarksView();

formView.listChildren.add( this._createBookmarksButton() );
}

Expand Down Expand Up @@ -299,6 +313,58 @@ export default class LinkUI extends Plugin {
return formView;
}

/**
* Creates a sorted array of buttons with bookmark names.
*/
private _createBookmarksListView(): Array<ButtonView> {
const editor = this.editor;
const bookmarkEditing = editor.plugins.get( 'BookmarkEditing' );
const bookmarksNames = Array.from( bookmarkEditing.getAllBookmarkNames() );

bookmarksNames.sort( ( a, b ) => a.localeCompare( b ) );

return bookmarksNames.map( bookmarkName => {
const buttonView = new ButtonView();

buttonView.set( {
label: bookmarkName,
tooltip: false,
icon: bookmarkIcon,
withText: true
} );

buttonView.on( 'execute', () => {
this.formView!.urlInputView.fieldView.value = '#' + bookmarkName;
// Set focus to the editing view to prevent from losing it while current view is removed.
editor.editing.view.focus();
this._balloon.remove( this.bookmarksView! );
// Set the focus to the URL input field.
this.formView!.focus();
} );

return buttonView;
} );
}

/**
* Creates a view for bookmarks.
*/
private _createBookmarksView(): LinkBookmarksView {
const editor = this.editor;
const view = new LinkBookmarksView( editor.locale );

// Hide the panel after clicking the "Cancel" button.
this.listenTo( view, 'cancel', () => {
// Set focus to the editing view to prevent from losing it while current view is removed.
editor.editing.view.focus();
this._balloon.remove( this.bookmarksView! );
// Set the focus to the URL input field.
this.formView!.focus();
} );

return view;
}

/**
* Creates the {@link module:link/ui/linkadvancedview~LinkAdvancedView} instance.
*/
Expand Down Expand Up @@ -401,6 +467,14 @@ export default class LinkUI extends Plugin {
label: t( 'Bookmarks' )
} );

this.listenTo( bookmarksButton, 'execute', () => {
this._balloon.add( {
view: this.bookmarksView!,
position: this._getBalloonPositionData()
} );
this.bookmarksView!.focus();
} );

return bookmarksButton;
}

Expand Down Expand Up @@ -528,6 +602,16 @@ export default class LinkUI extends Plugin {
}

const editor = this.editor;

if ( editor.plugins.has( 'BookmarkEditing' ) ) {
// To make bindings works.
// Clear the collection of bookmarks.
this.bookmarksView!.listChildren.clear();

// Add bookmarks to the collection.
this.bookmarksView!.listChildren.addMany( this._createBookmarksListView() );
}

const linkCommand: LinkCommand = editor.commands.get( 'link' )!;

this.formView!.disableCssTransitions();
Expand Down Expand Up @@ -585,6 +669,15 @@ export default class LinkUI extends Plugin {
}
}

/**
* Removes the {@link #bookmarksView} from the {@link #_balloon}.
*/
private _removeBookmarksView(): void {
if ( this._areBookmarksInPanel ) {
this._balloon.remove( this.bookmarksView! );
}
}

/**
* Removes the {@link #formView} from the {@link #_balloon}.
*/
Expand Down Expand Up @@ -674,7 +767,10 @@ export default class LinkUI extends Plugin {

// TODO: Remove dynamically registered views

// Remove the advanced form view first because it's on top of the stack.
// If the bookmarks view is visible, remove it because it can be on top of the stack.
this._removeBookmarksView();

// If the advanced form view is visible, remove it because it can be on top of the stack.
this._removeAdvancedView();

// Then remove the form view because it's beneath the advanced form.
Expand Down Expand Up @@ -749,6 +845,13 @@ export default class LinkUI extends Plugin {
return !!this.advancedView && this._balloon.hasView( this.advancedView );
}

/**
* Returns `true` when {@link #bookmarksView} is in the {@link #_balloon}.
*/
private get _areBookmarksInPanel(): boolean {
return !!this.bookmarksView && this._balloon.hasView( this.bookmarksView );
}

/**
* Returns `true` when {@link #formView} is in the {@link #_balloon}.
*/
Expand Down Expand Up @@ -788,18 +891,27 @@ export default class LinkUI extends Plugin {
}

/**
* Returns `true` when {@link #advancedView}, {@link #actionsView} or {@link #formView} is in the {@link #_balloon}.
* Returns `true` when {@link #bookmarksView} is in the {@link #_balloon} and it is
* currently visible.
*/
private get _areBookmarksVisible(): boolean {
return !!this.bookmarksView && this._balloon.visibleView === this.bookmarksView;
}

/**
* Returns `true` when {@link #advancedView}, {@link #actionsView}, {@link #bookmarksView}
* or {@link #formView} is in the {@link #_balloon}.
*/
private get _isUIInPanel(): boolean {
return this._isAdvancedInPanel || this._isFormInPanel || this._areActionsInPanel;
return this._isAdvancedInPanel || this._areBookmarksInPanel || this._isFormInPanel || this._areActionsInPanel;
}

/**
* Returns `true` when {@link #advancedView}, {@link #actionsView} or {@link #formView} is in the {@link #_balloon}
* and it is currently visible.
* Returns `true` when {@link #advancedView}, {@link #bookmarksView}, {@link #actionsView}
* or {@link #formView} is in the {@link #_balloon} and it is currently visible.
*/
private get _isUIVisible(): boolean {
return this._isAdvancedVisible || this._isFormVisible || this._areActionsVisible;
return this._isAdvancedVisible || this._areBookmarksVisible || this._isFormVisible || this._areActionsVisible;
}

/**
Expand Down
Loading