@ckeditor/ckeditor5-find-and-replace 29.1.0 → 31.1.0

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 (51) hide show
  1. package/build/find-and-replace.js +1 -1
  2. package/build/translations/da.js +1 -0
  3. package/build/translations/de.js +1 -0
  4. package/build/translations/es.js +1 -0
  5. package/build/translations/gl.js +1 -0
  6. package/build/translations/hu.js +1 -0
  7. package/build/translations/id.js +1 -0
  8. package/build/translations/it.js +1 -0
  9. package/build/translations/nl.js +1 -0
  10. package/build/translations/no.js +1 -0
  11. package/build/translations/pl.js +1 -0
  12. package/build/translations/pt-br.js +1 -0
  13. package/build/translations/ru.js +1 -0
  14. package/build/translations/sr-latn.js +1 -0
  15. package/build/translations/sr.js +1 -0
  16. package/build/translations/zh-cn.js +1 -0
  17. package/build/translations/zh.js +1 -0
  18. package/lang/contexts.json +4 -2
  19. package/lang/translations/da.po +69 -0
  20. package/lang/translations/de.po +69 -0
  21. package/lang/translations/en.po +12 -4
  22. package/lang/translations/es.po +69 -0
  23. package/lang/translations/gl.po +69 -0
  24. package/lang/translations/hu.po +12 -4
  25. package/lang/translations/id.po +69 -0
  26. package/lang/translations/it.po +69 -0
  27. package/lang/translations/nl.po +69 -0
  28. package/lang/translations/no.po +69 -0
  29. package/lang/translations/pl.po +69 -0
  30. package/lang/translations/pt-br.po +69 -0
  31. package/lang/translations/ru.po +69 -0
  32. package/lang/translations/sr-latn.po +69 -0
  33. package/lang/translations/sr.po +69 -0
  34. package/lang/translations/zh-cn.po +69 -0
  35. package/lang/translations/zh.po +69 -0
  36. package/package.json +20 -19
  37. package/src/findandreplace.js +15 -50
  38. package/src/findandreplaceediting.js +17 -6
  39. package/src/findandreplacestate.js +13 -1
  40. package/src/findandreplaceui.js +94 -95
  41. package/src/findcommand.js +18 -14
  42. package/src/findnextcommand.js +11 -8
  43. package/src/findpreviouscommand.js +4 -1
  44. package/src/replaceallcommand.js +3 -1
  45. package/src/replacecommand.js +11 -1
  46. package/src/ui/findandreplaceformview.js +591 -404
  47. package/src/utils.js +35 -22
  48. package/theme/findandreplace.css +2 -2
  49. package/theme/findandreplaceform.css +6 -215
  50. package/CHANGELOG.md +0 -4
  51. package/src/ui/checkboxview.js +0 -212
package/src/utils.js CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  /**
7
- * @module find-and-replace
7
+ * @module find-and-replace/utils
8
8
  */
9
9
 
10
10
  import { uid, Collection } from 'ckeditor5/src/utils';
@@ -13,11 +13,11 @@ import { escapeRegExp } from 'lodash-es';
13
13
  /**
14
14
  * Executes findCallback and updates search results list.
15
15
  *
16
- * @param {module:engine/model/range~Range} range
17
- * @param {module:engine/model/model~Model} model
18
- * @param {Function} findCallback
16
+ * @param {module:engine/model/range~Range} range The model range to scan for matches.
17
+ * @param {module:engine/model/model~Model} model The model.
18
+ * @param {Function} findCallback The callback that should return `true` if provided text matches the search term.
19
19
  * @param {module:utils/collection~Collection} [startResults] An optional collection of find matches that the function should
20
- * starts with. This would be a collection returned by a previous `updateFindResultFromRange()` call.
20
+ * start with. This would be a collection returned by a previous `updateFindResultFromRange()` call.
21
21
  * @returns {module:utils/collection~Collection} A collection of objects describing find match.
22
22
  *
23
23
  * An example structure:
@@ -33,20 +33,20 @@ import { escapeRegExp } from 'lodash-es';
33
33
  export function updateFindResultFromRange( range, model, findCallback, startResults ) {
34
34
  const results = startResults || new Collection();
35
35
 
36
- [ ...range ].forEach( ( { type, item } ) => {
37
- if ( type === 'elementStart' ) {
38
- if ( model.schema.checkChild( item, '$text' ) ) {
39
- const foundItems = findCallback( {
40
- item,
41
- text: rangeToText( model.createRangeIn( item ) )
42
- } );
36
+ model.change( writer => {
37
+ [ ...range ].forEach( ( { type, item } ) => {
38
+ if ( type === 'elementStart' ) {
39
+ if ( model.schema.checkChild( item, '$text' ) ) {
40
+ const foundItems = findCallback( {
41
+ item,
42
+ text: rangeToText( model.createRangeIn( item ) )
43
+ } );
43
44
 
44
- if ( !foundItems ) {
45
- return;
46
- }
45
+ if ( !foundItems ) {
46
+ return;
47
+ }
47
48
 
48
- foundItems.forEach( foundItem => {
49
- model.change( writer => {
49
+ foundItems.forEach( foundItem => {
50
50
  const resultId = `findResult:${ uid() }`;
51
51
  const marker = writer.addMarker( resultId, {
52
52
  usingOperation: false,
@@ -68,9 +68,9 @@ export function updateFindResultFromRange( range, model, findCallback, startResu
68
68
  index
69
69
  );
70
70
  } );
71
- } );
71
+ }
72
72
  }
73
- }
73
+ } );
74
74
  } );
75
75
 
76
76
  return results;
@@ -79,6 +79,9 @@ export function updateFindResultFromRange( range, model, findCallback, startResu
79
79
  /**
80
80
  * Returns text representation of a range. The returned text length should be the same as range length.
81
81
  * In order to achieve this this function will replace inline elements (text-line) as new line character ("\n").
82
+ *
83
+ * @param {module:engine/model/range~Range} range The model range.
84
+ * @returns {String} The text content of the provided range.
82
85
  */
83
86
  export function rangeToText( range ) {
84
87
  return Array.from( range.getItems() ).reduce( ( rangeText, node ) => {
@@ -93,6 +96,7 @@ export function rangeToText( range ) {
93
96
  }, '' );
94
97
  }
95
98
 
99
+ // Finds the appropriate index in the resultsList Collection.
96
100
  function findInsertIndex( resultsList, markerToInsert ) {
97
101
  const result = resultsList.find( ( { marker } ) => {
98
102
  return markerToInsert.getStart().isBefore( marker.getStart() );
@@ -101,12 +105,14 @@ function findInsertIndex( resultsList, markerToInsert ) {
101
105
  return result ? resultsList.getIndex( result ) : resultsList.length;
102
106
  }
103
107
 
108
+ // Maps RegExp match result to find result.
104
109
  function regexpMatchToFindResult( matchResult ) {
105
110
  const lastGroupIndex = matchResult.length - 1;
106
111
 
107
112
  let startOffset = matchResult.index;
108
113
 
109
114
  // Searches with match all flag have an extra matching group with empty string or white space matched before the word.
115
+ // If the search term starts with the space already, there is no extra group even with match all flag on.
110
116
  if ( matchResult.length === 3 ) {
111
117
  startOffset += matchResult[ 1 ].length;
112
118
  }
@@ -119,9 +125,10 @@ function regexpMatchToFindResult( matchResult ) {
119
125
  }
120
126
 
121
127
  /**
128
+ * Creates a text matching callback for a specified search term and matching options.
122
129
  *
123
- * @param {String} searchTerm
124
- * @param {Object} [options]
130
+ * @param {String} searchTerm The search term.
131
+ * @param {Object} [options] Matching options.
125
132
  * @param {Boolean} [options.matchCase=false] If set to `true` letter casing will be ignored.
126
133
  * @param {Boolean} [options.wholeWords=false] If set to `true` only whole words that match `callbackOrText` will be matched.
127
134
  * @returns {Function}
@@ -138,7 +145,13 @@ export function findByTextCallback( searchTerm, options ) {
138
145
  if ( options.wholeWords ) {
139
146
  const nonLetterGroup = '[^a-zA-Z\u00C0-\u024F\u1E00-\u1EFF]';
140
147
 
141
- regExpQuery = `(^|${ nonLetterGroup }|_)` + regExpQuery + `(?:_|${ nonLetterGroup }|$)`;
148
+ if ( !new RegExp( '^' + nonLetterGroup ).test( searchTerm ) ) {
149
+ regExpQuery = `(^|${ nonLetterGroup }|_)${ regExpQuery }`;
150
+ }
151
+
152
+ if ( !new RegExp( nonLetterGroup + '$' ).test( searchTerm ) ) {
153
+ regExpQuery = `${ regExpQuery }(?=_|${ nonLetterGroup }|$)`;
154
+ }
142
155
  }
143
156
 
144
157
  const regExp = new RegExp( regExpQuery, flags );
@@ -4,10 +4,10 @@
4
4
  */
5
5
 
6
6
  .ck-find-result {
7
- background: hsl(58, 100%, 50%);
7
+ background: hsl(60, 100%, 50%);
8
8
  color: var(--ck-color-text);
9
9
  }
10
10
 
11
11
  .ck-find-result_selected {
12
- background: hsl(52, 100%, 50%);
12
+ background: hsl(29, 100%, 60%);
13
13
  }
@@ -3,224 +3,15 @@
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
6
- /* stylelint-disable no-descending-specificity */
6
+ .ck.ck-find-and-replace-form {
7
+ max-width: 100%;
7
8
 
8
- /* disabled buttons bg color: #C3C3C3 */
9
- /* button color #4E8AE4 */
10
-
11
- @import "@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css";
12
-
13
- .ck .ck-find-and-replace-form .ck-find-form__wrapper,
14
- .ck .ck-find-and-replace-form .ck-replace-form__wrapper {
15
- display: flex;
16
- align-items: flex-start;
17
- flex-direction: row;
18
- flex-wrap: wrap;
19
-
20
- & .ck-button {
21
- margin-top: var(--ck-spacing-standard);
22
- background-color: var(--ck-color-base-active);
23
- padding: 5px 10px;
24
- text-transform: uppercase;
25
-
26
- &.ck-disabled {
27
- background-color: var(--ck-color-switch-button-off-background);
28
- }
29
-
30
- &:not(.ck-disabled) {
31
- color: var(--ck-color-base-background);
32
-
33
- &:hover {
34
- background-color: var(--ck-color-base-focus);
35
- cursor: pointer;
36
- }
37
- }
38
- }
39
-
40
- & .ck-labeled-field-view__input-wrapper .ck.ck-input-text{
41
- padding: var(--ck-spacing-extra-tiny) calc(var(--ck-spacing-medium) * 8) var(--ck-spacing-extra-tiny) var(--ck-spacing-medium);
42
- }
43
-
44
- @mixin ck-media-phone {
45
- flex-wrap: wrap;
46
-
47
- & .ck-labeled-field-view {
48
- flex-basis: 100%;
49
- }
50
-
51
- & .ck-button {
52
- flex-basis: 50%;
53
- }
54
- }
55
- }
56
-
57
- .ck .ck-find-and-replace-form .ck-replace-form__wrapper {
58
- justify-content: flex-end;
59
- border-top: 1px solid var(--ck-color-base-border);
60
-
61
- & .ck-labeled-field-view__input-wrapper .ck.ck-input-text{
62
- padding: var(--ck-spacing-extra-tiny) var(--ck-spacing-medium) var(--ck-spacing-extra-tiny) var(--ck-spacing-medium);
63
- }
64
-
65
- & .ck-button {
66
- border-radius: 5px;
67
- font-size: calc(var(--ck-font-size-normal) * 0.9);
68
-
69
- &:last-child {
70
- margin-left: var(--ck-spacing-standard)
71
- }
72
-
73
- & .ck.ck-button__label {
74
- line-height: 20px;
75
- }
76
- }
77
- }
78
-
79
- .ck .ck-find-and-replace-form .ck-find-form__wrapper {
80
- justify-content: space-between;
81
-
82
- & .ck-button.ck-button-prev,
83
- & .ck-button.ck-button-next {
84
- margin-left: 1px;
85
- }
86
-
87
- & span.ck-results-counter {
88
- position: absolute;
89
- top: calc(var(--ck-spacing-standard) * 2);
90
- right: calc(var(--ck-spacing-standard) * 3);
91
- color: var(--ck-color-base-border)
92
- }
93
-
94
- & .ck-find-buttons {
9
+ & fieldset {
95
10
  display: flex;
96
- align-items: center;
97
-
98
- & .ck-button {
99
- border-radius: 5px;
100
- padding: 5px;
101
-
102
- & svg.ck-button__icon {
103
- margin: 0;
104
- }
105
-
106
- &.ck-button-find {
107
- padding: 4px 16px;
108
- }
109
-
110
- &.ck-button-prev {
111
- border-radius: 5px 0 0 5px;
112
-
113
- & svg {
114
- transform: rotate(180deg) translateY(-2px);
115
- }
116
- }
117
11
 
118
- &.ck-button-next {
119
- border-radius: 0 5px 5px 0;
120
-
121
- & svg {
122
- transform: translateY(-1px);
123
- }
124
- }
125
- }
126
- }
127
-
128
- &.ck-is-searching {
129
- & .ck-button.ck-button-find {
130
- display: none;
131
- }
132
- }
133
-
134
- &:not(.ck-is-searching) {
135
- & .ck-button.ck-button-prev,
136
- & .ck-button.ck-button-next {
137
- display: none;
138
- }
139
-
140
- & .ck-button.ck-button-find {
141
- display: flex;
142
- }
143
- }
144
-
145
- &.ck-responsive-form .ck-find-checkboxes {
146
- margin: 0;
147
-
148
- & .ck-find-checkboxes__box {
149
- display: flex;
150
- align-items: center;
151
- max-width: 150px;
152
- overflow: hidden;
153
-
154
- & label {
155
- margin-left: 5px;
156
- line-height: calc(var(--ck-spacing-standard) * 2.2);
157
- overflow: hidden;
158
- text-overflow: ellipsis;
159
- }
160
-
161
- & label:hover,
162
- & input[type=checkbox]:hover
163
- {
164
- cursor: pointer;
165
- }
166
-
167
- &:first-child {
168
- margin-top: var(--ck-spacing-medium);
169
- }
170
-
171
- & input[type=checkbox]:disabled+label {
172
- color: var(--ck-color-switch-button-off-background);
173
- }
174
- }
175
- }
176
- }
177
-
178
- .ck[dir='rtl'] {
179
- & .ck .ck-find-and-replace-form .ck-find-form__wrapper .ck-labeled-field-view__input-wrapper .ck.ck-input-text {
180
- padding: var(--ck-spacing-extra-tiny) var(--ck-spacing-medium) var(--ck-spacing-extra-tiny) calc(var(--ck-spacing-medium) * 8);
181
- }
182
-
183
- & .ck .ck-find-and-replace-form .ck-replace-form__wrapper {
184
- & .ck-replace-buttons {
185
- display: flex;
186
- flex-flow: row-reverse;
187
-
188
- & .ck-button:last-child {
189
- margin-left: var(--ck-spacing-standard);
190
- }
191
- }
192
- & .ck-labeled-field-view__input-wrapper .ck.ck-input-text{
193
- padding: var(--ck-spacing-extra-tiny) var(--ck-spacing-medium);
194
- }
195
- }
196
-
197
- & .ck.ck-responsive-form > :not(:last-child) {
198
- margin-left: 0;
199
- }
200
-
201
- & .ck .ck-find-and-replace-form .ck-find-form__wrapper {
202
- & .ck-button.ck-button-prev,
203
- & .ck-button.ck-button-next {
204
- margin-right: 1px;
205
- margin-left: 0;
206
- }
207
-
208
- & span.ck-results-counter {
209
- left: calc(var(--ck-spacing-standard) * 3);
210
- }
211
-
212
- & .ck-find-buttons {
213
- flex-flow: row-reverse;
214
- }
215
-
216
- &.ck-responsive-form .ck-find-checkboxes {
217
- margin: 0;
218
-
219
- & .ck-find-checkboxes__box label {
220
- margin-right: 5px;
221
- }
12
+ /* The find fieldset */
13
+ &.ck-find-and-replace-form__find .ck-results-counter {
14
+ position: absolute;
222
15
  }
223
16
  }
224
17
  }
225
-
226
- /* stylelint-enable no-descending-specificity */
package/CHANGELOG.md DELETED
@@ -1,4 +0,0 @@
1
- Changelog
2
- =========
3
-
4
- All changes in the package are documented in the main repository. See: https://github.com/ckeditor/ckeditor5/blob/master/CHANGELOG.md.
@@ -1,212 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
-
6
- /**
7
- * @module find-and-replace/ui/checkboxview
8
- */
9
-
10
- import { View } from 'ckeditor5/src/ui';
11
- import { getCode } from 'ckeditor5/src/utils';
12
-
13
- /**
14
- * The checkbox view class.
15
- *
16
- * @extends module:ui/view~View
17
- */
18
- export default class CheckboxView extends View {
19
- /**
20
- * @inheritDoc
21
- */
22
- constructor( locale ) {
23
- super( locale );
24
-
25
- const bind = this.bindTemplate;
26
-
27
- /**
28
- * (Optional) The additional CSS class set on the button.
29
- *
30
- * @observable
31
- * @member {String} #class
32
- */
33
- this.set( 'class' );
34
-
35
- /**
36
- * Controls whether the checkbox view is enabled, i.e. it can be clicked and can execute an action.
37
- *
38
- * @observable
39
- * @default true
40
- * @member {Boolean} #isEnabled
41
- */
42
- this.set( 'isEnabled', true );
43
-
44
- /**
45
- * Controls whether the checkbox view is visible. Visible by default, the checkboxes are hidden
46
- * using a CSS class.
47
- *
48
- * @observable
49
- * @default true
50
- * @member {Boolean} #isVisible
51
- */
52
- this.set( 'isVisible', true );
53
-
54
- /**
55
- * Indicates whether a related checkbox is checked.
56
- *
57
- * @observable
58
- * @default false
59
- * @member {Boolean} #isChecked
60
- */
61
- this.set( 'isChecked', false );
62
-
63
- /**
64
- * The text of the label associated with the checkbox view.
65
- *
66
- * @observable
67
- * @member {String} #label
68
- */
69
- this.set( 'label' );
70
-
71
- /**
72
- * The HTML `id` attribute to be assigned to the checkbox.
73
- *
74
- * @observable
75
- * @default null
76
- * @member {String|null} #id
77
- */
78
- this.set( 'id', null );
79
-
80
- /**
81
- * (Optional) Controls the `tabindex` HTML attribute of the checkbox. By default, the checkbox is focusable
82
- * but is not included in the <kbd>Tab</kbd> order.
83
- *
84
- * @observable
85
- * @default -1
86
- * @member {String} #tabindex
87
- */
88
- this.set( 'tabindex', -1 );
89
-
90
- /**
91
- * The collection of the child views inside of the checkbox {@link #element}.
92
- *
93
- * @readonly
94
- * @member {module:ui/viewcollection~ViewCollection}
95
- */
96
- this.children = this.createCollection();
97
-
98
- /**
99
- * The label of the checkbox view. It is configurable using the {@link #label label attribute}.
100
- *
101
- * @readonly
102
- * @member {module:ui/view~View} #labelView
103
- */
104
- this.labelView = this._createLabelView( );
105
-
106
- /**
107
- * The input of the checkbox view.
108
- *
109
- * @readonly
110
- * @member {module:ui/view~View} #checkboxInputView
111
- */
112
- this.checkboxInputView = this._createCheckboxInputView();
113
-
114
- this.setTemplate( {
115
- tag: 'div',
116
-
117
- attributes: {
118
- class: [
119
- bind.to( 'class' ),
120
- bind.if( 'isEnabled', 'ck-disabled', value => !value ),
121
- bind.if( 'isVisible', 'ck-hidden', value => !value )
122
- ],
123
- tabindex: bind.to( 'tabindex' )
124
- },
125
-
126
- on: {
127
- keydown: bind.to( evt => {
128
- // Need to check target. Otherwise we would handle space press on input[type=text] and it would change
129
- // checked property twice due to default browser handling kicking in too.
130
- if ( evt.target === this.element && evt.keyCode == getCode( 'space' ) ) {
131
- this.isChecked = !this.isChecked;
132
- }
133
- } )
134
- },
135
-
136
- children: this.children
137
- } );
138
- }
139
-
140
- /**
141
- * @inheritDoc
142
- */
143
- render() {
144
- super.render();
145
-
146
- this.children.add( this.checkboxInputView );
147
- this.children.add( this.labelView );
148
- }
149
-
150
- /**
151
- * Focuses the {@link #element} of the checkbox.
152
- */
153
- focus() {
154
- this.element.focus();
155
- }
156
-
157
- /**
158
- * Creates a checkbox input view instance and binds it with checkbox attributes.
159
- *
160
- * @private
161
- * @returns {module:ui/view~View}
162
- */
163
- _createCheckboxInputView() {
164
- const checkboxInputView = new View();
165
- const bind = this.bindTemplate;
166
-
167
- checkboxInputView.setTemplate( {
168
- tag: 'input',
169
- attributes: {
170
- type: 'checkbox',
171
- id: bind.to( 'id' ),
172
- 'checked': bind.if( 'isChecked' ),
173
- 'disabled': bind.if( 'isEnabled', true, value => !value ),
174
- 'aria-disabled': bind.if( 'isEnabled', true, value => !value )
175
- },
176
-
177
- on: {
178
- change: bind.to( evt => {
179
- this.isChecked = evt.target.checked;
180
- } )
181
- }
182
- } );
183
-
184
- return checkboxInputView;
185
- }
186
-
187
- /**
188
- * Creates a label view instance and binds it with checkbox attributes.
189
- *
190
- * @private
191
- * @returns {module:ui/view~View}
192
- */
193
- _createLabelView() {
194
- const labelView = new View();
195
-
196
- labelView.setTemplate( {
197
- tag: 'label',
198
-
199
- attributes: {
200
- for: this.bindTemplate.to( 'id' )
201
- },
202
-
203
- children: [
204
- {
205
- text: this.bindTemplate.to( 'label' )
206
- }
207
- ]
208
- } );
209
-
210
- return labelView;
211
- }
212
- }