@ckeditor/ckeditor5-find-and-replace
Advanced tools
Comparing version 29.0.0 to 29.1.0
{ | ||
"name": "@ckeditor/ckeditor5-find-and-replace", | ||
"version": "29.0.0", | ||
"version": "29.1.0", | ||
"description": "Find and replace feature for CKEditor 5.", | ||
@@ -10,23 +10,28 @@ "keywords": [ | ||
"ckeditor5-feature", | ||
"ckeditor5-plugin" | ||
"ckeditor5-plugin", | ||
"ckeditor5-dll" | ||
], | ||
"main": "src/index.js", | ||
"dependencies": { | ||
"@ckeditor/ckeditor5-ui": "^29.0.0", | ||
"@ckeditor/ckeditor5-utils": "^29.0.0", | ||
"ckeditor5": "^29.0.0", | ||
"@ckeditor/ckeditor5-ui": "^29.1.0", | ||
"@ckeditor/ckeditor5-utils": "^29.1.0", | ||
"ckeditor5": "^29.1.0", | ||
"lodash-es": "^4.17.15" | ||
}, | ||
"devDependencies": { | ||
"@ckeditor/ckeditor5-dev-utils": "^25.0.0", | ||
"@ckeditor/ckeditor5-basic-styles": "^29.0.0", | ||
"@ckeditor/ckeditor5-core": "^29.0.0", | ||
"@ckeditor/ckeditor5-editor-classic": "^29.0.0", | ||
"@ckeditor/ckeditor5-editor-decoupled": "^29.0.0", | ||
"@ckeditor/ckeditor5-engine": "^29.0.0", | ||
"@ckeditor/ckeditor5-essentials": "^29.0.0", | ||
"@ckeditor/ckeditor5-font": "^29.0.0", | ||
"@ckeditor/ckeditor5-highlight": "^29.0.0", | ||
"@ckeditor/ckeditor5-paragraph": "^29.0.0", | ||
"@ckeditor/ckeditor5-theme-lark": "^29.0.0", | ||
"@ckeditor/ckeditor5-dev-utils": "^25.3.0", | ||
"@ckeditor/ckeditor5-basic-styles": "^29.1.0", | ||
"@ckeditor/ckeditor5-core": "^29.1.0", | ||
"@ckeditor/ckeditor5-editor-classic": "^29.1.0", | ||
"@ckeditor/ckeditor5-editor-decoupled": "^29.1.0", | ||
"@ckeditor/ckeditor5-engine": "^29.1.0", | ||
"@ckeditor/ckeditor5-essentials": "^29.1.0", | ||
"@ckeditor/ckeditor5-heading": "^29.1.0", | ||
"@ckeditor/ckeditor5-font": "^29.1.0", | ||
"@ckeditor/ckeditor5-highlight": "^29.1.0", | ||
"@ckeditor/ckeditor5-link": "^29.1.0", | ||
"@ckeditor/ckeditor5-list": "^29.1.0", | ||
"@ckeditor/ckeditor5-paragraph": "^29.1.0", | ||
"@ckeditor/ckeditor5-source-editing": "^29.1.0", | ||
"@ckeditor/ckeditor5-theme-lark": "^29.1.0", | ||
"webpack": "^4.43.0", | ||
@@ -52,3 +57,4 @@ "webpack-cli": "^3.3.11" | ||
"theme", | ||
"build" | ||
"build", | ||
"ckeditor5-metadata.json" | ||
], | ||
@@ -55,0 +61,0 @@ "scripts": { |
CKEditor 5 find and replace feature | ||
=========================== | ||
[![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-find-and-replace.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-find-and-replace) | ||
@@ -13,3 +12,3 @@ [![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5?branch=master) | ||
Check out the demo in the [Find and replace feature](https://ckeditor.com/docs/ckeditor5/latest/features/find-and-replace.html) guide. | ||
Check out the demo in the [find and replace feature guide](https://ckeditor.com/docs/ckeditor5/latest/features/find-and-replace.html#demo). | ||
@@ -16,0 +15,0 @@ ## Documentation |
@@ -17,4 +17,4 @@ /** | ||
import FindPreviousCommand from './findpreviouscommand'; | ||
import FindAndReplaceState from './findandreplacestate'; | ||
import { ObservableMixin, mix, Collection } from 'ckeditor5/src/utils'; | ||
// eslint-disable-next-line ckeditor5-rules/ckeditor-imports | ||
@@ -27,74 +27,2 @@ import { scrollViewportToShowTarget } from '@ckeditor/ckeditor5-utils/src/dom/scroll'; | ||
/** | ||
* The object storing find & replace plugin state in a given editor instance. | ||
* | ||
*/ | ||
class FindAndReplaceState { | ||
constructor( model ) { | ||
this.set( 'results', new Collection() ); | ||
this.set( 'highlightedResult', null ); | ||
this.set( 'searchText', '' ); | ||
this.set( 'replaceText', '' ); | ||
this.set( 'matchCase', false ); | ||
this.set( 'matchWholeWords', false ); | ||
this.results.on( 'change', ( eventInfo, { removed, index } ) => { | ||
removed = Array.from( removed ); | ||
if ( removed.length ) { | ||
let highlightedResultRemoved = false; | ||
model.change( writer => { | ||
for ( const removedResult of removed ) { | ||
if ( this.highlightedResult === removedResult ) { | ||
highlightedResultRemoved = true; | ||
} | ||
if ( model.markers.has( removedResult.marker.name ) ) { | ||
writer.removeMarker( removedResult.marker ); | ||
} | ||
} | ||
} ); | ||
if ( highlightedResultRemoved ) { | ||
const nextHighlightedIndex = index >= this.results.length ? 0 : index; | ||
this.highlightedResult = this.results.get( nextHighlightedIndex ); | ||
} | ||
} | ||
} ); | ||
} | ||
clear( model ) { | ||
// @todo: actually this handling might be moved to editing part. | ||
// This could be a results#change listener that would ensure that related markers are ALWAYS removed without | ||
// having to call state.clear() explicitly. | ||
this.searchText = ''; | ||
// this.replaceText = ''; | ||
model.change( writer => { | ||
if ( this.highlightedResult ) { | ||
const oldMatchId = this.highlightedResult.marker.name.split( ':' )[ 1 ]; | ||
const oldMarker = model.markers.get( `findResultHighlighted:${ oldMatchId }` ); | ||
if ( oldMarker ) { | ||
writer.removeMarker( oldMarker ); | ||
} | ||
} | ||
[ ...this.results ].forEach( ( { marker } ) => { | ||
writer.removeMarker( marker ); | ||
} ); | ||
} ); | ||
this.results.clear(); | ||
} | ||
} | ||
mix( FindAndReplaceState, ObservableMixin ); | ||
const HIGHLIGHT_CLASS = 'ck-find-result_selected'; | ||
@@ -176,3 +104,3 @@ | ||
* | ||
* @member {module:find-and-replace/findandreplaceediting~FindAndReplaceState} #state | ||
* @member {module:find-and-replace/findandreplacestate~FindAndReplaceState} #state | ||
*/ | ||
@@ -179,0 +107,0 @@ this.state = new FindAndReplaceState( this.editor.model ); |
@@ -11,3 +11,3 @@ /** | ||
import { Plugin } from 'ckeditor5/src/core'; | ||
import { createDropdown, SplitButtonView } from 'ckeditor5/src/ui'; | ||
import { createDropdown } from 'ckeditor5/src/ui'; | ||
import FindAndReplaceFormView from './ui/findandreplaceformview'; | ||
@@ -47,9 +47,5 @@ // See: #8833. | ||
this.set( 'isSearching', false ); | ||
this.set( 'matchCount', null ); | ||
this.set( 'highlightOffset', null ); | ||
this.bind( 'isSearching' ).to( this, 'matchCount', count => count > 0 ); | ||
/** | ||
@@ -72,5 +68,5 @@ * The form view will only be assigned if the find and replace toolbar button was added. | ||
editor.ui.componentFactory.add( 'findAndReplace', locale => { | ||
const dropdown = createDropdown( locale, SplitButtonView ); | ||
const dropdown = createDropdown( locale ); | ||
const formView = new FindAndReplaceFormView( editor.locale ); | ||
const formView = new FindAndReplaceFormView( editor.locale, this._state ); | ||
@@ -85,4 +81,2 @@ formView.delegate( 'findNext' ).to( this ); | ||
formView.bind( 'isSearching' ).to( this ); | ||
this._createToolbarDropdown( dropdown, loupeIcon, formView ); | ||
@@ -100,27 +94,4 @@ | ||
if ( this._state ) { | ||
this.unbind( 'isSearching' ); | ||
const findTextInputView = formView.findInputView.fieldView; | ||
// Searching should only be active if there's more than 1 result matched and | ||
// the user has not changed any search criteria. | ||
this.bind( 'isSearching' ).to( this, 'matchCount', | ||
findTextInputView, 'value', this._state, 'searchText', | ||
formView.matchCaseView, 'isChecked', this._state, 'matchCase', | ||
formView.matchWholeWordsView, 'isChecked', this._state, 'matchWholeWords', | ||
( count, | ||
viewSearchText, modelSearchText, | ||
viewMatchCase, modelMatchCase, | ||
viewWholeWords, modelWholeWords | ||
) => { | ||
return count > 0 && | ||
viewSearchText == modelSearchText && | ||
viewMatchCase == modelMatchCase && | ||
viewWholeWords == modelWholeWords; | ||
} ); | ||
} | ||
editor.keystrokes.set( 'Ctrl+F', ( data, cancelEvent ) => { | ||
dropdown.buttonView.actionView.fire( 'execute' ); | ||
dropdown.buttonView.fire( 'execute' ); | ||
@@ -130,2 +101,5 @@ cancelEvent(); | ||
// Dropdown should be disabled when in source editing mode. See #10001. | ||
dropdown.bind( 'isEnabled' ).to( editor.commands.get( 'find' ) ); | ||
return dropdown; | ||
@@ -139,3 +113,3 @@ } ); | ||
* @protected | ||
* @param {module:find-and-replace/findandreplaceediting~FindAndReplaceState} state State object to be tracked. | ||
* @param {module:find-and-replace/findandreplacestate~FindAndReplaceState} state State object to be tracked. | ||
*/ | ||
@@ -184,12 +158,18 @@ _setState( state ) { | ||
icon, | ||
tooltip: t( 'Find and replace' ) | ||
label: t( 'Find and replace' ), | ||
keystroke: 'CTRL+F', | ||
tooltip: true | ||
} ); | ||
// Clicking the main button has the same effect as clicking the dropdown arrow. | ||
buttonView.actionView.delegate( 'execute' ).to( buttonView.arrowView ); | ||
// Each time a dropdown is opened, the search text field should get focused. | ||
// Note: Use the low priority to make sure the following listener starts working after the | ||
// default action of the drop-down is executed (i.e. the panel showed up). Otherwise, the | ||
// invisible form/input cannot be focused/selected. | ||
buttonView.on( 'open', () => { | ||
formView.disableCssTransitions(); | ||
formView.findInputView.fieldView.select(); | ||
formView.focus(); | ||
formView.enableCssTransitions(); | ||
}, { priority: 'low' } ); | ||
@@ -196,0 +176,0 @@ } |
@@ -45,3 +45,3 @@ /** | ||
* @param {Object} [options] | ||
* @param {Boolean} [options.matchCase=false] If set to `true`, the letter case will be ignored. | ||
* @param {Boolean} [options.matchCase=false] If set to `true`, the letter case will be matched. | ||
* @param {Boolean} [options.wholeWords=false] If set to `true`, only whole words that match `callbackOrText` will be matched. | ||
@@ -62,23 +62,19 @@ * @fires execute | ||
} else { | ||
// @todo: disable callback version | ||
findCallback = callbackOrText; | ||
} | ||
// Initial search is done on all nodes inside the content. | ||
const range = model.createRangeIn( model.document.getRoot() ); | ||
// Initial search is done on all nodes in all roots inside the content. | ||
const results = model.document.getRootNames() | ||
.reduce( ( ( currentResults, rootName ) => updateFindResultFromRange( | ||
model.createRangeIn( model.document.getRoot( rootName ) ), | ||
model, | ||
findCallback, | ||
currentResults | ||
) ), null ); | ||
// @todo: fix me | ||
// this.listenTo( model.document, 'change:data', () => onDocumentChange( results, model, findCallback ) ); | ||
const ret = { | ||
results: updateFindResultFromRange( range, model, findCallback ), | ||
findCallback | ||
}; | ||
this.state.clear( model ); | ||
this.state.results.addMany( Array.from( ret.results ) ); | ||
this.state.highlightedResult = ret.results.get( 0 ); | ||
this.state.results.addMany( Array.from( results ) ); | ||
this.state.highlightedResult = results.get( 0 ); | ||
if ( typeof callbackOrText === 'string' ) { | ||
// @todo: eliminate this code repetition. Done to fix unit tests. | ||
this.state.searchText = callbackOrText; | ||
@@ -90,4 +86,7 @@ } | ||
return ret; | ||
return { | ||
results, | ||
findCallback | ||
}; | ||
} | ||
} |
@@ -32,3 +32,3 @@ /** | ||
* @private | ||
* @member {module:find-and-replace/findandreplaceediting~FindAndReplaceState} #_state | ||
* @member {module:find-and-replace/findandreplacestate~FindAndReplaceState} #_state | ||
*/ | ||
@@ -35,0 +35,0 @@ this._state = state; |
@@ -40,9 +40,14 @@ /** | ||
const { model } = editor; | ||
const range = model.createRangeIn( model.document.getRoot() ); | ||
const results = textToReplace instanceof Collection ? | ||
textToReplace : updateFindResultFromRange( range, model, findByTextCallback( textToReplace, this._state ) ); | ||
textToReplace : model.document.getRootNames() | ||
.reduce( ( ( currentResults, rootName ) => updateFindResultFromRange( | ||
model.createRangeIn( model.document.getRoot( rootName ) ), | ||
model, | ||
findByTextCallback( textToReplace, this._state ), | ||
currentResults | ||
) ), null ); | ||
if ( results.length ) { | ||
this.editor.model.change( () => { | ||
model.change( () => { | ||
[ ...results ].forEach( searchResult => { | ||
@@ -49,0 +54,0 @@ // Just reuse logic from the replace command to replace a single match. |
@@ -33,3 +33,3 @@ /** | ||
* @private | ||
* @member {module:find-and-replace/findandreplaceediting~FindAndReplaceState} #_state | ||
* @member {module:find-and-replace/findandreplacestate~FindAndReplaceState} #_state | ||
*/ | ||
@@ -36,0 +36,0 @@ this._state = state; |
@@ -10,3 +10,12 @@ /** | ||
import { ButtonView, FocusCycler, LabeledFieldView, createLabeledInputText, View, submitHandler, ViewCollection } from 'ckeditor5/src/ui'; | ||
import { | ||
ButtonView, | ||
FocusCycler, | ||
LabeledFieldView, | ||
createLabeledInputText, | ||
View, | ||
submitHandler, | ||
ViewCollection, | ||
injectCssTransitionDisabler | ||
} from 'ckeditor5/src/ui'; | ||
import { FocusTracker, KeystrokeHandler, uid } from 'ckeditor5/src/utils'; | ||
@@ -20,3 +29,3 @@ | ||
import findArrowIcon from '@ckeditor/ckeditor5-ui/theme/icons/dropdown-arrow.svg'; | ||
import CheckboxView from '../ui/checkboxview'; | ||
import CheckboxView from './checkboxview'; | ||
@@ -31,3 +40,9 @@ /** | ||
export default class FindAndReplaceFormView extends View { | ||
constructor( locale ) { | ||
/** | ||
* Creates a view of find and replace form. | ||
* | ||
* @param {module:utils/locale~Locale} [locale] The localization services instance. | ||
* @param {module:find-and-replace/findandreplacestate~FindAndReplaceState} state State object. | ||
*/ | ||
constructor( locale, state ) { | ||
super( locale ); | ||
@@ -44,186 +59,114 @@ | ||
*/ | ||
this.set( 'isSearching' ); | ||
this.set( 'searchText', '' ); | ||
this.set( 'replaceText', '' ); | ||
this.set( 'isSearching', false ); | ||
/** | ||
* Stores the number of matched search results. | ||
* Searched text value. | ||
* | ||
* @readonly | ||
* @observable | ||
* @member {Number} #matchCount | ||
* @member {String} #searchText | ||
*/ | ||
this.set( 'matchCount', null ); | ||
this.set( 'searchText', '' ); | ||
/** | ||
* The offset of currently highlighted search result in {@link #matchCount matched results}. | ||
* Replace text value. | ||
* | ||
* @readonly | ||
* @observable | ||
* @member {String} #replaceText | ||
*/ | ||
this.set( 'replaceText', '' ); | ||
/** | ||
* Indicates whether search text, match case or whole word checkbox in form | ||
* is different from the values in | ||
* {@link module:find-and-replace/findandreplaceediting~FindAndReplaceEditing#state the editing state}. | ||
* | ||
* If `true` it means that the user made some changes to the form without actually triggering the search. | ||
* | ||
* @readonly | ||
* @observable | ||
* @member {Number|null} #highlightOffset | ||
* @member {Boolean} #isDirty | ||
*/ | ||
this.set( 'highlightOffset', null ); | ||
this.set( 'isDirty', false ); | ||
/** | ||
* Whether the search results counter should be visible. | ||
* Indicates if the matchCase checkbox has been checked. | ||
* | ||
* @private | ||
* @readonly | ||
* @observable | ||
* @member {Boolean} #isCounterHidden | ||
* @member {Boolean} #matchCase | ||
*/ | ||
this.set( 'isCounterHidden', true ); | ||
this.set( 'matchCase', false ); | ||
/** | ||
* The find in text input view that stores the searched string. | ||
* | ||
* @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView} | ||
*/ | ||
this.findInputView = this._createInputField( t( 'Find in text…' ) ); | ||
* Indicates if the matchWholeWords checkbox has been checked. | ||
* | ||
* @readonly | ||
* @observable | ||
* @member {Boolean} #matchWholeWords | ||
*/ | ||
this.set( 'matchWholeWords', false ); | ||
/** | ||
* The find button view that initializes the search process. | ||
* Stores the number of matched search results. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.findButtonView = this._createButton( t( 'Find' ), 'ck-button-find' ); | ||
this.findButtonView.on( 'execute', () => { | ||
this.fire( 'findNext', { | ||
searchText: this.searchText, | ||
matchCase: this.matchCaseView.isChecked, | ||
wholeWords: this.matchWholeWordsView.isChecked | ||
} ); | ||
} ); | ||
/** | ||
* The find previous button view. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.findPrevButtonView = this._createButton( t( 'Previous result' ), 'ck-button-prev', findArrowIcon, false ); | ||
this.findPrevButtonView.on( 'execute', () => { | ||
this.fire( 'findPrevious' ); | ||
} ); | ||
/** | ||
* The find next button view. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.findNextButtonView = this._createButton( t( 'Next result' ), 'ck-button-next', findArrowIcon, false ); | ||
this.findNextButtonView.on( 'execute', () => { | ||
this.fire( 'findNext' ); | ||
} ); | ||
/** | ||
* The replace button view. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.replaceButtonView = this._createButton( t( 'Replace' ), 'ck-button-replace' ); | ||
this.replaceButtonView.on( 'execute', () => { | ||
this.fire( 'replace', { searchText: this.searchText, replaceText: this.replaceText } ); | ||
} ); | ||
/** | ||
* The replace all button view. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.replaceAllButtonView = this._createButton( t( 'Replace all' ), 'ck-button-replaceall' ); | ||
this.replaceAllButtonView.on( 'execute', () => { | ||
this.fire( 'replaceAll', { searchText: this.searchText, replaceText: this.replaceText } ); | ||
} ); | ||
/** | ||
* The match case checkbox view. | ||
* | ||
* @member {module:find-and-replace/ui/checkboxview~CheckboxView} | ||
*/ | ||
this.matchCaseView = this._createCheckbox( t( 'Match case' ) ); | ||
/** | ||
* The whole words only checkbox view. | ||
* | ||
* @member {module:find-and-replace/ui/checkboxview~CheckboxView} | ||
*/ | ||
this.matchWholeWordsView = this._createCheckbox( t( 'Whole words only' ) ); | ||
/** | ||
* The replace input view. | ||
* | ||
* @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView} | ||
*/ | ||
this.replaceInputView = this._createInputField( t( 'Replace with…' ) ); | ||
/** | ||
* Stores gathered views related to find functionality of the feature. | ||
* | ||
* @member {module:ui/view~View} | ||
*/ | ||
this.findView = this._createFindView(); | ||
/** | ||
* Stores gathered views related to replace functionality of the feature. | ||
* | ||
* @member {module:ui/view~View} | ||
*/ | ||
this.replaceView = this._createReplaceView(); | ||
/** | ||
* Tracks information about the DOM focus in the form. | ||
* | ||
* @readonly | ||
* @member {module:utils/focustracker~FocusTracker} | ||
* @observable | ||
* @member {Number} #matchCount | ||
*/ | ||
this.focusTracker = new FocusTracker(); | ||
this.set( 'matchCount', null ); | ||
/** | ||
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. | ||
* The offset of currently highlighted search result in {@link #matchCount matched results} or | ||
* `null` if there's no highlighted result. | ||
* | ||
* @readonly | ||
* @member {module:utils/keystrokehandler~KeystrokeHandler} | ||
* @observable | ||
* @member {Number|null} #highlightOffset | ||
*/ | ||
this.keystrokes = new KeystrokeHandler(); | ||
this.set( 'highlightOffset', null ); | ||
/** | ||
* A collection of views that can be focused in the form. | ||
* Whether the search results counter should be visible. | ||
* | ||
* @private | ||
* @readonly | ||
* @protected | ||
* @member {module:ui/viewcollection~ViewCollection} | ||
* @observable | ||
* @member {Boolean} #isCounterHidden | ||
*/ | ||
this._focusables = new ViewCollection(); | ||
this.set( 'isCounterHidden', true ); | ||
/** | ||
* Helps cycling over {@link #_focusables} in the form. | ||
* | ||
* @readonly | ||
* @protected | ||
* @member {module:ui/focuscycler~FocusCycler} | ||
*/ | ||
this._focusCycler = new FocusCycler( { | ||
focusables: this._focusables, | ||
focusTracker: this.focusTracker, | ||
keystrokeHandler: this.keystrokes, | ||
actions: { | ||
// Navigate form fields backwards using the <kbd>Shift</kbd> + <kbd>Tab</kbd> keystroke. | ||
focusPrevious: 'shift + tab', | ||
this._assignViews( t ); | ||
// Navigate form fields forwards using the <kbd>Tab</kbd> key. | ||
focusNext: 'tab' | ||
this._initFocusables(); | ||
this.bind( 'isDirty' ).to( | ||
this, 'searchText', state, 'searchText', | ||
this.matchCaseView, 'isChecked', state, 'matchCase', | ||
this.matchWholeWordsView, 'isChecked', state, 'matchWholeWords', | ||
( formSearchText, stateSearchText, formMatchCase, stateMatchCase, formMatchWholeWords, stateMatchWholeWords ) => { | ||
return formSearchText !== stateSearchText || | ||
formMatchCase !== stateMatchCase || | ||
formMatchWholeWords !== stateMatchWholeWords; | ||
} | ||
} ); | ||
); | ||
this.bind( 'searchText' ).to( this.findInputView.fieldView, 'value' ); | ||
this.bind( 'searchText' ).to( this.findInputView.fieldView, 'value', enforceStringValue ); | ||
this.findButtonView.bind( 'isEnabled' ).to( this.findInputView.fieldView, 'isEmpty', value => !value ); | ||
this.bind( 'replaceText' ).to( this.replaceInputView.fieldView, 'value' ); | ||
this.bind( 'replaceText' ).to( this.replaceInputView.fieldView, 'value', enforceStringValue ); | ||
this.replaceButtonView.bind( 'isEnabled' ).to( this, 'isSearching' ); | ||
this.replaceAllButtonView.bind( 'isEnabled' ).to( this, 'isSearching' ); | ||
this.bind( 'isCounterHidden' ).to( this, 'matchCount', this, 'highlightOffset', ( matchCount, highlightOffset ) => { | ||
return matchCount === null || matchCount === 0 || | ||
highlightOffset === null || highlightOffset === 0; | ||
this.bind( 'isCounterHidden' ).to( state, 'searchText', this, 'isDirty', ( searchText, isDirty ) => { | ||
return searchText == '' || isDirty; | ||
} ); | ||
this.bind( 'isSearching' ).to( | ||
this, 'matchCount', this, 'isDirty', | ||
( count, isDirty ) => { | ||
return count > 0 && !isDirty; | ||
} | ||
); | ||
this.setTemplate( { | ||
@@ -244,2 +187,9 @@ tag: 'form', | ||
} ); | ||
function enforceStringValue( value ) { | ||
// For search / replace text let's enforce strings for easier comparison. | ||
return value === undefined ? '' : value; | ||
} | ||
injectCssTransitionDisabler( this ); | ||
} | ||
@@ -296,3 +246,2 @@ | ||
this.keystrokes.set( 'enter', event => { | ||
// @todo: this is a bit workaroundish way to handle enter, we should work on views rather than raw DOM elements. | ||
const target = event.target; | ||
@@ -343,5 +292,161 @@ | ||
/** | ||
* A collection of views for the 'find' functionality of the feature | ||
* Initialization of focusables elements of the form. | ||
* | ||
* @private | ||
*/ | ||
_initFocusables() { | ||
/** | ||
* Tracks information about the DOM focus in the form. | ||
* | ||
* @readonly | ||
* @member {module:utils/focustracker~FocusTracker} | ||
*/ | ||
this.focusTracker = new FocusTracker(); | ||
/** | ||
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. | ||
* | ||
* @readonly | ||
* @member {module:utils/keystrokehandler~KeystrokeHandler} | ||
*/ | ||
this.keystrokes = new KeystrokeHandler(); | ||
/** | ||
* A collection of views that can be focused in the form. | ||
* | ||
* @readonly | ||
* @protected | ||
* @member {module:ui/viewcollection~ViewCollection} | ||
*/ | ||
this._focusables = new ViewCollection(); | ||
/** | ||
* Helps cycling over {@link #_focusables} in the form. | ||
* | ||
* @readonly | ||
* @protected | ||
* @member {module:ui/focuscycler~FocusCycler} | ||
*/ | ||
this._focusCycler = new FocusCycler( { | ||
focusables: this._focusables, | ||
focusTracker: this.focusTracker, | ||
keystrokeHandler: this.keystrokes, | ||
actions: { | ||
// Navigate form fields backwards using the <kbd>Shift</kbd> + <kbd>Tab</kbd> keystroke. | ||
focusPrevious: 'shift + tab', | ||
// Navigate form fields forwards using the <kbd>Tab</kbd> key. | ||
focusNext: 'tab' | ||
} | ||
} ); | ||
} | ||
/** | ||
* Assigning form element views. | ||
* | ||
* @private | ||
* @param {Function} t Used for translating message to the uiLanguage. | ||
*/ | ||
_assignViews( t ) { | ||
/** | ||
* The find in text input view that stores the searched string. | ||
* | ||
* @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView} | ||
*/ | ||
this.findInputView = this._createInputField( t( 'Find in text…' ) ); | ||
/** | ||
* The find button view that initializes the search process. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.findButtonView = this._createButton( t( 'Find' ), 'ck-button-find' ); | ||
this.findButtonView.on( 'execute', () => { | ||
this.fire( 'findNext', { | ||
searchText: this.searchText, | ||
matchCase: this.matchCaseView.isChecked, | ||
wholeWords: this.matchWholeWordsView.isChecked | ||
} ); | ||
} ); | ||
/** | ||
* The find previous button view. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.findPrevButtonView = this._createButton( t( 'Previous result' ), 'ck-button-prev', findArrowIcon, false ); | ||
this.findPrevButtonView.on( 'execute', () => { | ||
this.fire( 'findPrevious' ); | ||
} ); | ||
/** | ||
* The find next button view. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.findNextButtonView = this._createButton( t( 'Next result' ), 'ck-button-next', findArrowIcon, false ); | ||
this.findNextButtonView.on( 'execute', () => { | ||
this.fire( 'findNext' ); | ||
} ); | ||
/** | ||
* The replace button view. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.replaceButtonView = this._createButton( t( 'Replace' ), 'ck-button-replace' ); | ||
this.replaceButtonView.on( 'execute', () => { | ||
this.fire( 'replace', { searchText: this.searchText, replaceText: this.replaceText } ); | ||
} ); | ||
/** | ||
* The replace all button view. | ||
* | ||
* @member {module:ui/button/buttonview~ButtonView} | ||
*/ | ||
this.replaceAllButtonView = this._createButton( t( 'Replace all' ), 'ck-button-replaceall' ); | ||
this.replaceAllButtonView.on( 'execute', () => { | ||
this.fire( 'replaceAll', { searchText: this.searchText, replaceText: this.replaceText } ); | ||
} ); | ||
/** | ||
* The match case checkbox view. | ||
* | ||
* @member {module:find-and-replace/ui/checkboxview~CheckboxView} | ||
*/ | ||
this.matchCaseView = this._createCheckbox( t( 'Match case' ) ); | ||
/** | ||
* The whole words only checkbox view. | ||
* | ||
* @member {module:find-and-replace/ui/checkboxview~CheckboxView} | ||
*/ | ||
this.matchWholeWordsView = this._createCheckbox( t( 'Whole words only' ) ); | ||
/** | ||
* The replace input view. | ||
* | ||
* @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView} | ||
*/ | ||
this.replaceInputView = this._createInputField( t( 'Replace with…' ) ); | ||
/** | ||
* Stores gathered views related to find functionality of the feature. | ||
* | ||
* @member {module:ui/view~View} | ||
*/ | ||
this.findView = this._createFindView(); | ||
/** | ||
* Stores gathered views related to replace functionality of the feature. | ||
* | ||
* @member {module:ui/view~View} | ||
*/ | ||
this.replaceView = this._createReplaceView(); | ||
} | ||
/** | ||
* A collection of views for the 'find' functionality of the feature. | ||
* | ||
* @private | ||
* @return {module:ui/view~View} The find view instance. | ||
@@ -378,3 +483,3 @@ */ | ||
{ | ||
text: bind.to( 'highlightOffset' ) | ||
text: bind.to( 'highlightOffset', highlightOffset => highlightOffset || 0 ) | ||
}, | ||
@@ -417,3 +522,3 @@ t( ' of ' ), | ||
/** | ||
* A collection of views for the 'replace' functionality of the feature | ||
* A collection of views for the 'replace' functionality of the feature. | ||
* | ||
@@ -468,3 +573,2 @@ * @private | ||
// @todo: this looks like an upstream UI bug (the fact that InputTextView#value does not get updated). | ||
inputField.on( 'input', () => { | ||
@@ -531,3 +635,3 @@ inputField.value = inputField.element.value; | ||
/** | ||
* Fired when the find next button ({@link #findNextButtonView}) is triggered . | ||
* Fired when the find next button ({@link #findNextButtonView}) is triggered. | ||
* | ||
@@ -534,0 +638,0 @@ * @event findNext |
@@ -81,5 +81,3 @@ /** | ||
* Returns text representation of a range. The returned text length should be the same as range length. | ||
* In order to achieve this this function will: | ||
* - replace inline elements (text-line) as new line character ("\n"). | ||
* - @todo: check unicode characters | ||
* In order to achieve this this function will replace inline elements (text-line) as new line character ("\n"). | ||
*/ | ||
@@ -86,0 +84,0 @@ export function rangeToText( range ) { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
109917
25
1949
17
21
Updatedckeditor5@^29.1.0