@1024pix/pix-ui 67.0.3 → 68.0.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.
@@ -1,3 +1,4 @@
1
+ import { hash } from '@ember/helper';
1
2
  import { action } from '@ember/object';
2
3
  import { guidFor } from '@ember/object/internals';
3
4
  import { inject as service } from '@ember/service';
@@ -109,12 +110,11 @@ export default class PixFilterableAndSearchableSelect extends Component {
109
110
  </PixMultiSelect>
110
111
  <PixSelect
111
112
  @id={{this.pixSelectId}}
112
- @placeholder={{@placeholder}}
113
+ @texts={{hash placeholder=@placeholder selectSearchLabel=@searchLabel}}
113
114
  @value={{@value}}
114
115
  @options={{this.selectableOptions}}
115
116
  @onChange={{@onChange}}
116
117
  @isSearchable={{@isSearchable}}
117
- @searchLabel={{@searchLabel}}
118
118
  @screenReaderOnly={{true}}
119
119
  @hideDefaultOption={{@hideDefaultOption}}
120
120
  @className="pix-filterable-and-searchable-select__pix-select"
@@ -13,26 +13,66 @@ import { gt, or } from 'ember-truth-helpers';
13
13
  import onArrowDownUpAction from '../modifiers/on-arrow-down-up-action';
14
14
  import onEnterAction from '../modifiers/on-enter-action';
15
15
  import onEscapeAction from '../modifiers/on-escape-action';
16
- import { formatMessage } from '../translations';
17
16
  import PixCheckbox from './pix-checkbox';
18
17
  import PixIcon from './pix-icon';
19
18
  import PixLabel from './pix-label';
20
19
 
21
- function removeCapitalizeAndDiacritics(string) {
22
- return string
23
- .normalize('NFD')
24
- .replace(/[\u0300-\u036f]/g, '')
25
- .toLowerCase();
26
- }
20
+ /**
21
+ * @typedef {object} PixMultiSelectOption
22
+ * @property {string} value - Valeur de l'option.
23
+ * @property {string} label - Texte affiché.
24
+ */
25
+
26
+ /**
27
+ * @typedef {object} PixMultiSelectTexts
28
+ * @property {string} placeholder - Texte affiché tant qu'aucune option n'est sélectionnée. Obligatoire.
29
+ * @property {string} [requiredLabel] - Rend le champ obligatoire et affiche un astérisque, dont ce texte est l'infobulle.
30
+ * @property {string} [subLabel] - Complément d'information affiché sous le libellé.
31
+ * @property {string} [searchLabel] - Intitulé du champ de recherche, lu par les lecteurs d'écran (accessible uniquement).
32
+ * @property {string} [searchPlaceholder] - Texte indicatif affiché dans le champ de recherche.
33
+ * @property {string} [emptySearchMessage] - Message affiché quand aucune option ne correspond à la recherche.
34
+ */
35
+
36
+ /**
37
+ * @typedef {object} PixMultiSelectArgs
38
+ * @property {PixMultiSelectOption[]} options - Options proposées. Obligatoire.
39
+ * @property {string[]} [values] - Valeurs des options sélectionnées.
40
+ * @property {PixMultiSelectTexts} texts - Textes du composant. Obligatoire.
41
+ * @property {(values: string[]) => unknown} [onChange] - Appelée avec la liste complète des valeurs sélectionnées à chaque changement.
42
+ * @property {string} [id] - Identifiant du champ. Généré automatiquement s'il n'est pas fourni.
43
+ * @property {boolean} [isDisabled] - Désactive le champ.
44
+ * @property {boolean} [isSearchable] - Ajoute un champ de recherche en tête de liste.
45
+ * @property {(value: string) => unknown} [onSearch] - Prend en charge la recherche à la place du filtrage interne, pour interroger un serveur par exemple.
46
+ * @property {'small' | 'default' | 'large'} [size] - Taille du libellé. Par défaut : `default`.
47
+ * @property {boolean} [screenReaderOnly] - Masque le libellé visuellement, tout en le laissant lisible par les lecteurs d'écran.
48
+ * @property {boolean} [inlineLabel] - Place le libellé sur la même ligne que le champ.
49
+ * @property {string} [className] - Classes CSS ajoutées au bouton d'ouverture.
50
+ * @property {string} [placement] - Position de la liste par rapport au champ, au sens de Popper. Par défaut : `bottom-start`.
51
+ * @property {boolean} [isComputeWidthDisabled] - Désactive l'alignement automatique de la largeur du champ sur celle de la liste.
52
+ */
53
+
54
+ /**
55
+ * @typedef {object} PixMultiSelectSignature
56
+ * @property {HTMLDivElement} Element
57
+ * @property {PixMultiSelectArgs} Args
58
+ * @property {{ label: [], placeholder: [], default: [PixMultiSelectOption] }} Blocks
59
+ */
27
60
 
28
61
  export default class PixMultiSelect extends Component {
29
62
  @tracked isExpanded = false;
30
- @tracked searchData;
31
63
  @service elementHelper;
32
64
 
33
65
  constructor(...args) {
34
66
  super(...args);
35
67
 
68
+ warn(
69
+ 'PixMultiSelect: @texts.placeholder attribute is mandatory for usability. if you not using placeholder {{yield}}',
70
+ Boolean(this.args.texts?.placeholder),
71
+ {
72
+ id: 'pix-ui.select-placeholder.mandatory',
73
+ },
74
+ );
75
+
36
76
  this.searchId = 'search-input-' + guidFor(this);
37
77
  this.multiSelectId = this.args.id ? this.args.id : 'select-' + guidFor(this);
38
78
  this.listId = `list-${this.multiSelectId}`;
@@ -49,14 +89,6 @@ export default class PixMultiSelect extends Component {
49
89
  element.style.setProperty('--pix-multi-select-width', `${selectWidth}rem`);
50
90
  });
51
91
  }
52
-
53
- warn(
54
- `PixMultiSelect: @strictSearch is deprecated in favour of @onSearch`,
55
- !this.args.strictSearch,
56
- {
57
- id: 'pix-ui.pix-multi-select.strictSearch.deprecated',
58
- },
59
- );
60
92
  }
61
93
 
62
94
  get options() {
@@ -73,19 +105,36 @@ export default class PixMultiSelect extends Component {
73
105
  return classes;
74
106
  }
75
107
 
108
+ get searchLabel() {
109
+ return this.args.texts?.searchLabel;
110
+ }
111
+
112
+ get searchPlaceholder() {
113
+ return this.args.texts?.searchPlaceholder;
114
+ }
115
+
116
+ get emptySearchMessage() {
117
+ return this.args.texts?.emptySearchMessage;
118
+ }
119
+
120
+ get requiredLabel() {
121
+ return this.args.texts?.requiredLabel;
122
+ }
123
+
124
+ get subLabel() {
125
+ return this.args.texts?.subLabel;
126
+ }
127
+
76
128
  get isAriaExpanded() {
77
129
  return this.isExpanded ? 'true' : 'false';
78
130
  }
79
131
 
80
132
  get results() {
81
- if (this.args.isSearchable && this.searchData) {
82
- return this.args.options.filter(({ label }) => this._search(label));
83
- }
84
133
  return this.args.options;
85
134
  }
86
135
 
87
136
  get placeholder() {
88
- const { values, placeholder } = this.args;
137
+ const { values, texts } = this.args;
89
138
  if (values?.length > 0) {
90
139
  const selectedOptionLabels = this.options
91
140
  .filter((option) => {
@@ -96,14 +145,7 @@ export default class PixMultiSelect extends Component {
96
145
  .join(', ');
97
146
  return selectedOptionLabels;
98
147
  }
99
- return placeholder;
100
- }
101
-
102
- _search(label) {
103
- if (this.args.strictSearch) {
104
- return label.includes(this.searchData);
105
- }
106
- return removeCapitalizeAndDiacritics(label).includes(this.searchData);
148
+ return texts?.placeholder;
107
149
  }
108
150
 
109
151
  @action
@@ -148,13 +190,7 @@ export default class PixMultiSelect extends Component {
148
190
 
149
191
  @action
150
192
  updateSearch(event) {
151
- if (this.args.onSearch) {
152
- this.args.onSearch(event.target.value);
153
- } else {
154
- this.searchData = this.args.strictSearch
155
- ? event.target.value
156
- : removeCapitalizeAndDiacritics(event.target.value);
157
- }
193
+ this.args.onSearch(event.target.value);
158
194
  }
159
195
 
160
196
  @action
@@ -167,10 +203,6 @@ export default class PixMultiSelect extends Component {
167
203
  return ' ' + className;
168
204
  }
169
205
 
170
- get selectSearchLabel() {
171
- return formatMessage(this.args.locale ?? 'fr', 'select.search');
172
- }
173
-
174
206
  @action
175
207
  focus(event) {
176
208
  if (!event.target) return;
@@ -192,8 +224,8 @@ export default class PixMultiSelect extends Component {
192
224
  >
193
225
  <PixLabel
194
226
  @for={{this.multiSelectId}}
195
- @requiredLabel={{@requiredLabel}}
196
- @subLabel={{@subLabel}}
227
+ @requiredLabel={{this.requiredLabel}}
228
+ @subLabel={{this.subLabel}}
197
229
  @size={{@size}}
198
230
  @screenReaderOnly={{@screenReaderOnly}}
199
231
  @inlineLabel={{@inlineLabel}}
@@ -216,7 +248,7 @@ export default class PixMultiSelect extends Component {
216
248
  >
217
249
  {{#if (has-block "placeholder")}}
218
250
  <span class="pix-multi-select__placeholder">{{yield to="placeholder"}}</span>
219
- {{else if @placeholder}}
251
+ {{else}}
220
252
  <span class="pix-multi-select__placeholder">{{this.placeholder}}</span>
221
253
  {{/if}}
222
254
  <PixIcon
@@ -239,14 +271,14 @@ export default class PixMultiSelect extends Component {
239
271
  <li class="pix-select__search">
240
272
  <PixIcon class="pix-select-search__icon" @name="search" @ariaHidden={{true}} />
241
273
  <label class="screen-reader-only" for={{this.searchId}}>
242
- {{this.selectSearchLabel}}
274
+ {{this.searchLabel}}
243
275
  </label>
244
276
  <input
245
277
  class="pix-select-search__input"
246
278
  id={{this.searchId}}
247
279
  autocomplete="off"
248
280
  tabindex={{if this.isExpanded "0" "-1"}}
249
- placeholder={{@searchPlaceholder}}
281
+ placeholder={{this.searchPlaceholder}}
250
282
  {{on "input" this.updateSearch}}
251
283
  />
252
284
  </li>
@@ -271,7 +303,7 @@ export default class PixMultiSelect extends Component {
271
303
  {{else}}
272
304
  <li
273
305
  class="pix-multi-select-list__item pix-multi-select-list__item--no-result"
274
- >{{@emptyMessage}}</li>
306
+ >{{this.emptySearchMessage}}</li>
275
307
  {{/if}}
276
308
  </ul>
277
309
  </PopperJS>
@@ -1,3 +1,4 @@
1
+ import { warn } from '@ember/debug';
1
2
  import { on } from '@ember/modifier';
2
3
  import { action } from '@ember/object';
3
4
  import { guidFor } from '@ember/object/internals';
@@ -10,11 +11,59 @@ import { or } from 'ember-truth-helpers';
10
11
 
11
12
  import onArrowDownUpAction from '../modifiers/on-arrow-down-up-action';
12
13
  import onEscapeAction from '../modifiers/on-escape-action';
13
- import { formatMessage } from '../translations';
14
14
  import PixIcon from './pix-icon';
15
15
  import PixLabel from './pix-label';
16
16
  import PixSelectList from './pix-select-list';
17
17
 
18
+ /**
19
+ * @typedef {object} PixSelectOption
20
+ * @property {string} value - Valeur de l'option.
21
+ * @property {string} label - Texte affiché.
22
+ * @property {string} [category] - Regroupe l'option sous une catégorie, affichée comme intertitre dans la liste.
23
+ * @property {string} [icon] - Nom d'une icône affichée avant le texte.
24
+ * @property {string} [iconTitle] - Description de l'icône pour les lecteurs d'écran.
25
+ */
26
+
27
+ /**
28
+ * @typedef {object} PixSelectTexts
29
+ * @property {string} placeholder - Texte affiché tant qu'aucune option n'est sélectionnée. Obligatoire.
30
+ * @property {string} [requiredLabel] - Rend le champ obligatoire et affiche un astérisque, dont ce texte est l'infobulle.
31
+ * @property {string} [subLabel] - Complément d'information affiché sous le libellé.
32
+ * @property {string} [selectSearchLabel] - Intitulé du champ de recherche, lu par les lecteurs d'écran (accessible uniquement).
33
+ * @property {string} [searchPlaceholder] - Texte indicatif affiché dans le champ de recherche.
34
+ * @property {string} [emptySearchMessage] - Message affiché quand aucune option ne correspond à la recherche.
35
+ */
36
+
37
+ /**
38
+ * @typedef {object} PixSelectArgs
39
+ * @property {PixSelectOption[]} options - Options proposées. Obligatoire.
40
+ * @property {(value: string) => unknown} onChange - Appelée avec la valeur de l'option choisie. Obligatoire.
41
+ * @property {PixSelectTexts} texts - Textes du composant. Obligatoire.
42
+ * @property {string} [value] - Valeur de l'option sélectionnée.
43
+ * @property {string} [id] - Identifiant du champ. Généré automatiquement s'il n'est pas fourni.
44
+ * @property {boolean} [isDisabled] - Désactive le champ.
45
+ * @property {boolean} [isSearchable] - Ajoute un champ de recherche en tête de liste.
46
+ * @property {(value: string) => unknown} [onSearch] - Prend en charge la recherche à la place du filtrage interne, pour interroger un serveur par exemple.
47
+ * @property {boolean} [hideDefaultOption] - Retire l'option qui permet de revenir à « aucun choix ».
48
+ * @property {string} [iconName] - Nom d'une icône affichée dans le champ, avant le texte.
49
+ * @property {boolean} [plainIcon] - Affiche `iconName` dans sa variante pleine.
50
+ * @property {'small' | 'default' | 'large'} [size] - Taille du libellé. Par défaut : `default`.
51
+ * @property {boolean} [screenReaderOnly] - Masque le libellé visuellement, tout en le laissant lisible par les lecteurs d'écran.
52
+ * @property {boolean} [inlineLabel] - Place le libellé sur la même ligne que le champ.
53
+ * @property {boolean} [isFullWidth] - Étend le champ à toute la largeur disponible.
54
+ * @property {string} [errorMessage] - Message d'erreur affiché sous le champ. Sa présence applique le style d'erreur.
55
+ * @property {string} [className] - Classes CSS ajoutées au bouton d'ouverture.
56
+ * @property {string} [placement] - Position de la liste par rapport au champ, au sens de Popper. Par défaut : `bottom-start`.
57
+ * @property {boolean} [isComputeWidthDisabled] - Désactive l'alignement automatique de la largeur du champ sur celle de la liste.
58
+ */
59
+
60
+ /**
61
+ * @typedef {object} PixSelectSignature
62
+ * @property {HTMLDivElement} Element
63
+ * @property {PixSelectArgs} Args
64
+ * @property {{ label: [] }} Blocks
65
+ */
66
+
18
67
  export default class PixSelect extends Component {
19
68
  @service elementHelper;
20
69
  @tracked isExpanded = false;
@@ -22,6 +71,13 @@ export default class PixSelect extends Component {
22
71
 
23
72
  constructor(...args) {
24
73
  super(...args);
74
+ warn(
75
+ 'PixSelect: @texts.placeholder attribute is mandatory for usability.',
76
+ Boolean(this.args.texts?.placeholder),
77
+ {
78
+ id: 'pix-ui.select-placeholder.mandatory',
79
+ },
80
+ );
25
81
 
26
82
  this.searchId = 'search-input-' + guidFor(this);
27
83
  this.selectId = this.args.id ? this.args.id : 'select-' + guidFor(this);
@@ -41,10 +97,6 @@ export default class PixSelect extends Component {
41
97
  }
42
98
  }
43
99
 
44
- get selectSearchLabel() {
45
- return formatMessage(this.args.locale ?? 'fr', 'select.search');
46
- }
47
-
48
100
  get displayDefaultOption() {
49
101
  return !this.searchValue && !this.args.hideDefaultOption;
50
102
  }
@@ -79,9 +131,9 @@ export default class PixSelect extends Component {
79
131
  }
80
132
 
81
133
  get placeholder() {
82
- if (!this.args.value) return this.args.placeholder;
134
+ if (!this.args.value) return this.args.texts?.placeholder;
83
135
  const option = this.args.options.find((option) => option.value === this.args.value);
84
- return option ? option.label : this.args.placeholder;
136
+ return option ? option.label : this.args.texts?.placeholder;
85
137
  }
86
138
 
87
139
  get defaultOption() {
@@ -170,8 +222,8 @@ export default class PixSelect extends Component {
170
222
  {{#if (has-block "label")}}
171
223
  <PixLabel
172
224
  @for={{this.selectId}}
173
- @requiredLabel={{@requiredLabel}}
174
- @subLabel={{@subLabel}}
225
+ @requiredLabel={{@texts.requiredLabel}}
226
+ @subLabel={{@texts.subLabel}}
175
227
  @size={{@size}}
176
228
  @screenReaderOnly={{@screenReaderOnly}}
177
229
  @inlineLabel={{@inlineLabel}}
@@ -219,14 +271,14 @@ export default class PixSelect extends Component {
219
271
  <div class="pix-select__search">
220
272
  <PixIcon class="pix-select-search__icon" @name="search" @ariaHidden={{true}} />
221
273
  <label class="screen-reader-only" for={{this.searchId}}>
222
- {{this.selectSearchLabel}}
274
+ {{@texts.selectSearchLabel}}
223
275
  </label>
224
276
  <input
225
277
  class="pix-select-search__input"
226
278
  id={{this.searchId}}
227
279
  autocomplete="off"
228
280
  tabindex={{if this.isExpanded "0" "-1"}}
229
- placeholder={{@searchPlaceholder}}
281
+ placeholder={{@texts.searchPlaceholder}}
230
282
  {{on "input" this.setSearchValue}}
231
283
  />
232
284
  </div>
@@ -242,8 +294,8 @@ export default class PixSelect extends Component {
242
294
  @selectId={{this.selectId}}
243
295
  @isExpanded={{this.isExpanded}}
244
296
  @options={{@options}}
245
- @defaultOptionValue={{@placeholder}}
246
- @emptySearchMessage={{@emptySearchMessage}}
297
+ @defaultOptionValue={{@texts.placeholder}}
298
+ @emptySearchMessage={{@texts.emptySearchMessage}}
247
299
  />
248
300
  </div>
249
301
  </PopperJS>
@@ -1,4 +1 @@
1
- // WORKAROUND: necessary for storybook to resolve the import
2
- import '@formatjs/intl';
3
-
4
1
  export { default } from '@1024pix/pix-ui/components/pix-checkbox';
@@ -1,4 +1 @@
1
- // WORKAROUND: necessary for storybook to resolve the import
2
- import '@formatjs/intl';
3
-
4
1
  export { default } from '@1024pix/pix-ui/components/pix-gauge';
@@ -1,4 +1 @@
1
- // WORKAROUND: necessary for storybook to resolve the import
2
- import '@formatjs/intl';
3
-
4
1
  export { default } from '@1024pix/pix-ui/components/pix-pagination';
@@ -1,4 +1 @@
1
- // WORKAROUND: necessary for storybook to resolve the import
2
- import '@formatjs/intl';
3
-
4
1
  export { default } from '@1024pix/pix-ui/components/pix-radio-button';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1024pix/pix-ui",
3
- "version": "67.0.3",
3
+ "version": "68.0.0",
4
4
  "description": "Pix-UI is the implementation of Pix design principles and guidelines for its products.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -61,7 +61,6 @@
61
61
  },
62
62
  "dependencies": {
63
63
  "@babel/core": "^7.25.2",
64
- "@formatjs/intl": "^4.0.0",
65
64
  "check-engine": "^1.14.0",
66
65
  "ember-auto-import": "^2.7.4",
67
66
  "ember-cli-babel": "^8.2.0",
@@ -117,7 +116,6 @@
117
116
  "eslint": "^9.21.0",
118
117
  "eslint-config-prettier": "^10.0.2",
119
118
  "eslint-plugin-ember": "^12.5.0",
120
- "eslint-plugin-i18n-json": "^4.0.1",
121
119
  "eslint-plugin-n": "^17.16.2",
122
120
  "eslint-plugin-prettier": "^5.2.3",
123
121
  "eslint-plugin-qunit": "^8.1.2",
@@ -1,5 +0,0 @@
1
- export default {
2
- select: {
3
- search: 'Search',
4
- },
5
- };
@@ -1,5 +0,0 @@
1
- export default {
2
- select: {
3
- search: 'Buscar',
4
- },
5
- };
@@ -1,5 +0,0 @@
1
- export default {
2
- select: {
3
- search: 'Buscar',
4
- },
5
- };
@@ -1,5 +0,0 @@
1
- export default {
2
- select: {
3
- search: 'Rechercher',
4
- },
5
- };
@@ -1,49 +0,0 @@
1
- import { createIntl } from '@formatjs/intl';
2
-
3
- import en from './en';
4
- import es from './es';
5
- import es419 from './es-419';
6
- import fr from './fr';
7
- import nl from './nl';
8
-
9
- export function formatMessage(locale, message, values) {
10
- const intl = locales[locale] || locales.en;
11
- return intl.formatMessage({ id: message }, values);
12
- }
13
-
14
- const locales = {
15
- fr: createIntl({
16
- locale: 'fr',
17
- messages: flattenObject(fr),
18
- }),
19
- en: createIntl({
20
- locale: 'en',
21
- messages: flattenObject(en),
22
- }),
23
- nl: createIntl({
24
- locale: 'nl',
25
- messages: flattenObject(nl),
26
- }),
27
- es: createIntl({
28
- locale: 'es',
29
- messages: flattenObject(es),
30
- }),
31
- 'es-419': createIntl({
32
- locale: 'es-419',
33
- messages: flattenObject(es419),
34
- }),
35
- };
36
-
37
- export function flattenObject(object) {
38
- const entries = Object.entries(object);
39
-
40
- const flatEntries = entries.flatMap(([key, value]) => {
41
- if (typeof value !== 'object') return [[key, value]];
42
-
43
- const childEntries = Object.entries(flattenObject(value));
44
-
45
- return childEntries.map(([childKey, childValue]) => [`${key}.${childKey}`, childValue]);
46
- });
47
-
48
- return Object.fromEntries(flatEntries);
49
- }
@@ -1,5 +0,0 @@
1
- export default {
2
- select: {
3
- search: 'Zoeken',
4
- },
5
- };