@limetech/lime-elements 38.28.0 → 38.28.2

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,4 +1,4 @@
1
- import { Host, h, } from '@stencil/core';
1
+ import { Host, h } from '@stencil/core';
2
2
  import { getIconName } from '../icon/get-icon-props';
3
3
  import { createRandomString } from '../../util/random-string';
4
4
  import { CheckboxTemplate } from '../checkbox/checkbox.template';
@@ -109,84 +109,6 @@ export class ListItemComponent {
109
109
  }
110
110
  return (h(CheckboxTemplate, { id: `checkbox_${this.labelId}`, checked: this.selected, disabled: this.disabled }));
111
111
  };
112
- this.onClick = (event) => {
113
- if (this.disabled) {
114
- // Ignore toggling, but don't block embedded controls
115
- return;
116
- }
117
- const target = event.target;
118
- const cameFromActionTrigger = !!(target === null || target === void 0 ? void 0 : target.closest('.action-menu-trigger'));
119
- const cameFromNoToggle = !!(target === null || target === void 0 ? void 0 : target.closest('[data-no-toggle]'));
120
- const cameFromMenu = !!(target === null || target === void 0 ? void 0 : target.closest('limel-menu'));
121
- if (cameFromActionTrigger || cameFromNoToggle || cameFromMenu) {
122
- return;
123
- }
124
- if (this.isSelectableType()) {
125
- this.handleInteraction();
126
- }
127
- // For non-selectable types (menuitem/listitem), allow native click to bubble
128
- };
129
- this.onKeyDown = (event) => {
130
- if (this.disabled) {
131
- return;
132
- }
133
- // Only handle keyboard when the host itself has focus.
134
- // This avoids toggling when Space/Enter is pressed on inner controls
135
- // like the action menu trigger or any primary component.
136
- const shadowRoot = this.host.shadowRoot;
137
- const activeElement = shadowRoot
138
- ? shadowRoot.activeElement
139
- : null;
140
- if (activeElement && activeElement !== this.host) {
141
- return;
142
- }
143
- const isEnter = event.key === 'Enter';
144
- const isSpace = event.key === ' ' ||
145
- event.key === 'Space' ||
146
- event.key === 'Spacebar' ||
147
- event.code === 'Space';
148
- if (!isEnter && !isSpace) {
149
- return;
150
- }
151
- // Avoid re-triggering while key is held down and auto-repeats
152
- if (event.repeat) {
153
- // Also prevent default scroll on Space when repeating
154
- if (isSpace) {
155
- event.preventDefault();
156
- }
157
- return;
158
- }
159
- // Prevent page scroll and default button behavior on Space
160
- if (isSpace) {
161
- event.preventDefault();
162
- }
163
- if (this.isSelectableType()) {
164
- this.handleInteraction();
165
- return;
166
- }
167
- // For non-selectable items, treat Enter and Space as activation (simulate click)
168
- if (isEnter || isSpace) {
169
- this.host.click();
170
- }
171
- };
172
- this.handleInteraction = () => {
173
- const newSelected = !this.selected;
174
- const item = {
175
- text: this.text,
176
- secondaryText: this.secondaryText,
177
- disabled: this.disabled,
178
- icon: this.icon,
179
- selected: newSelected,
180
- value: this.value,
181
- actions: this.actions,
182
- primaryComponent: this.primaryComponent,
183
- image: this.image,
184
- };
185
- this.interact.emit({
186
- selected: newSelected,
187
- item: item,
188
- });
189
- };
190
112
  this.actionMenuLabel = () => {
191
113
  return translate.get('file-viewer.more-actions', this.language);
192
114
  };
@@ -226,7 +148,7 @@ export class ListItemComponent {
226
148
  }
227
149
  return (h(Host, Object.assign({ role: this.getHostRole(), class: {
228
150
  'has-primary-component': !!((_a = this.primaryComponent) === null || _a === void 0 ? void 0 : _a.name),
229
- } }, ariaProps, { onClick: this.onClick, onKeyDown: this.onKeyDown }), this.renderRadioButton(), this.renderCheckbox(), this.renderIcon(), this.renderImage(), this.renderPrimaryComponent(), h("div", { class: "text" }, this.renderLabel(), this.renderDescription()), this.renderActionMenu(this.actions)));
151
+ } }, ariaProps), this.renderRadioButton(), this.renderCheckbox(), this.renderIcon(), this.renderImage(), this.renderPrimaryComponent(), h("div", { class: "text" }, this.renderLabel(), this.renderDescription()), this.renderActionMenu(this.actions)));
230
152
  }
231
153
  /**
232
154
  * Returns a stable reference for the provided actions array to avoid
@@ -242,11 +164,6 @@ export class ListItemComponent {
242
164
  this.memoizedActions = actions;
243
165
  return actions;
244
166
  }
245
- isSelectableType() {
246
- return (this.type === 'option' ||
247
- this.type === 'radio' ||
248
- this.type === 'checkbox');
249
- }
250
167
  getHostRole() {
251
168
  switch (this.type) {
252
169
  case 'option': {
@@ -532,29 +449,5 @@ export class ListItemComponent {
532
449
  }
533
450
  };
534
451
  }
535
- static get events() {
536
- return [{
537
- "method": "interact",
538
- "name": "interact",
539
- "bubbles": true,
540
- "cancelable": true,
541
- "composed": true,
542
- "docs": {
543
- "tags": [],
544
- "text": "Emitted when the list item toggles selection (only for selectable types and not disabled)."
545
- },
546
- "complexType": {
547
- "original": "{\n selected: boolean;\n item: ListItem;\n }",
548
- "resolved": "{ selected: boolean; item: ListItem<any>; }",
549
- "references": {
550
- "ListItem": {
551
- "location": "import",
552
- "path": "./list-item.types"
553
- }
554
- }
555
- }
556
- }];
557
- }
558
- static get elementRef() { return "host"; }
559
452
  }
560
453
  //# sourceMappingURL=list-item.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"list-item.js","sourceRoot":"","sources":["../../../src/components/list-item/list-item.tsx"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,CAAC,EACD,KAAK,EAEL,OAAO,GACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAI9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAClF,OAAO,SAAS,MAAM,2BAA2B,CAAC;AAGlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAMH,MAAM,OAAO,iBAAiB;EA+G1B;IA+CQ,gBAAW,GAAG,GAAG,EAAE;MACvB,OAAO,CACH,YAAM,KAAK,EAAC,OAAO,EAAC,EAAE,EAAE,IAAI,CAAC,OAAO,IAC/B,IAAI,CAAC,IAAI,CACP,CACV,CAAC;IACN,CAAC,CAAC;IAEM,sBAAiB,GAAG,GAAG,EAAE;MAC7B,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;QACrB,OAAO;OACV;MAED,OAAO,CACH,YAAM,KAAK,EAAC,aAAa,EAAC,EAAE,EAAE,IAAI,CAAC,aAAa,IAC3C,IAAI,CAAC,aAAa,CAChB,CACV,CAAC;IACN,CAAC,CAAC;IAEM,eAAU,GAAG,GAAG,EAAE;MACtB,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;MACxC,IAAI,CAAC,QAAQ,EAAE;QACX,OAAO;OACV;MAED,IAAI,SAA6B,CAAC;MAClC,IAAI,mBAAuC,CAAC;MAC5C,IAAI,KAAyB,CAAC;MAE9B,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE;QAC/B,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAC5B,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;QAChD,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;OAC3B;MAED,MAAM,SAAS,GAAG;QACd,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;QACpC,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE;UACH,KAAK,EAAE,SAAS;UAChB,kBAAkB,EAAE,mBAAmB;SAC1C;QACD,KAAK,EAAE,IAAI,CAAC,SAAS;QACrB,IAAI,EAAE,IAAI,CAAC,QAAQ;OACtB,CAAC;MAEF,OAAO,kCAAgB,SAAS,EAAI,CAAC;IACzC,CAAC,CAAC;IAEM,2BAAsB,GAAG,GAAG,EAAE;MAClC,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC;MACtC,IAAI,CAAC,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI,CAAA,EAAE;QAChB,OAAO;OACV;MAED,MAAM,gBAAgB,GAAQ,OAAO,CAAC,IAAI,CAAC;MAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;MAElC,OAAO,EAAC,gBAAgB,oBAAK,KAAK,EAAI,CAAC;IAC3C,CAAC,CAAC;IAEM,gBAAW,GAAG,GAAG,EAAE;MACvB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;QACb,OAAO;OACV;MAED,OAAO,WAAK,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,EAAC,MAAM,GAAG,CAAC;IAC5E,CAAC,CAAC;IAEM,qBAAgB,GAAG,CAAC,OAAwC,EAAE,EAAE;MACpE,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;QAClC,OAAO;OACV;MAED,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;MACrD,OAAO,CACH,kBACI,KAAK,EAAC,gCAAgC,EACtC,KAAK,EAAE,aAAa,EACpB,aAAa,EAAC,YAAY;QAE1B,yBACI,KAAK,EAAC,qBAAqB,EAC3B,IAAI,EAAC,SAAS,EACd,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,GAC/B,CACO,CAChB,CAAC;IACN,CAAC,CAAC;IAmBM,sBAAiB,GAAG,GAAG,EAAE;MAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE;QACvB,OAAO;OACV;MAED,OAAO,CACH,EAAC,mBAAmB,IAChB,EAAE,EAAE,SAAS,IAAI,CAAC,OAAO,EAAE,EAC3B,OAAO,EAAE,IAAI,CAAC,QAAQ,EACtB,QAAQ,EAAE,IAAI,CAAC,QAAQ,GACzB,CACL,CAAC;IACN,CAAC,CAAC;IAEM,mBAAc,GAAG,GAAG,EAAE;MAC1B,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE;QAC1B,OAAO;OACV;MAED,OAAO,CACH,EAAC,gBAAgB,IACb,EAAE,EAAE,YAAY,IAAI,CAAC,OAAO,EAAE,EAC9B,OAAO,EAAE,IAAI,CAAC,QAAQ,EACtB,QAAQ,EAAE,IAAI,CAAC,QAAQ,GACzB,CACL,CAAC;IACN,CAAC,CAAC;IAEM,YAAO,GAAG,CAAC,KAAiB,EAAE,EAAE;MACpC,IAAI,IAAI,CAAC,QAAQ,EAAE;QACf,qDAAqD;QACrD,OAAO;OACV;MAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAA4B,CAAC;MAClD,MAAM,qBAAqB,GAAG,CAAC,CAAC,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA,CAAC;MACxE,MAAM,gBAAgB,GAAG,CAAC,CAAC,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,CAAC,kBAAkB,CAAC,CAAA,CAAC;MAC/D,MAAM,YAAY,GAAG,CAAC,CAAC,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,CAAC,YAAY,CAAC,CAAA,CAAC;MACrD,IAAI,qBAAqB,IAAI,gBAAgB,IAAI,YAAY,EAAE;QAC3D,OAAO;OACV;MAED,IAAI,IAAI,CAAC,gBAAgB,EAAE,EAAE;QACzB,IAAI,CAAC,iBAAiB,EAAE,CAAC;OAC5B;MACD,6EAA6E;IACjF,CAAC,CAAC;IAEM,cAAS,GAAG,CAAC,KAAoB,EAAE,EAAE;MACzC,IAAI,IAAI,CAAC,QAAQ,EAAE;QACf,OAAO;OACV;MAED,uDAAuD;MACvD,qEAAqE;MACrE,yDAAyD;MACzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;MACxC,MAAM,aAAa,GAAG,UAAU;QAC5B,CAAC,CAAE,UAAU,CAAC,aAAoC;QAClD,CAAC,CAAC,IAAI,CAAC;MACX,IAAI,aAAa,IAAI,aAAa,KAAK,IAAI,CAAC,IAAI,EAAE;QAC9C,OAAO;OACV;MAED,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC;MACtC,MAAM,OAAO,GACT,KAAK,CAAC,GAAG,KAAK,GAAG;QACjB,KAAK,CAAC,GAAG,KAAK,OAAO;QACrB,KAAK,CAAC,GAAG,KAAK,UAAU;QACxB,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;MAE3B,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE;QACtB,OAAO;OACV;MAED,8DAA8D;MAC9D,IAAI,KAAK,CAAC,MAAM,EAAE;QACd,sDAAsD;QACtD,IAAI,OAAO,EAAE;UACT,KAAK,CAAC,cAAc,EAAE,CAAC;SAC1B;QACD,OAAO;OACV;MAED,2DAA2D;MAC3D,IAAI,OAAO,EAAE;QACT,KAAK,CAAC,cAAc,EAAE,CAAC;OAC1B;MAED,IAAI,IAAI,CAAC,gBAAgB,EAAE,EAAE;QACzB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,OAAO;OACV;MAED,iFAAiF;MACjF,IAAI,OAAO,IAAI,OAAO,EAAE;QACpB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;OACrB;IACL,CAAC,CAAC;IA8BM,sBAAiB,GAAG,GAAG,EAAE;MAC7B,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;MAEnC,MAAM,IAAI,GAAa;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,WAAW;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,KAAK,EAAE,IAAI,CAAC,KAAK;OACpB,CAAC;MAEF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QACf,QAAQ,EAAE,WAAW;QACrB,IAAI,EAAE,IAAI;OACb,CAAC,CAAC;IACP,CAAC,CAAC;IAEM,oBAAe,GAAG,GAAW,EAAE;MACnC,OAAO,SAAS,CAAC,GAAG,CAAC,0BAA0B,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpE,CAAC,CAAC;oBA7Z2B,IAAI;;;;oBAwBf,KAAK;;oBAYK,OAAO;qBAMhB,KAAK;oBAMN,KAAK;;;;gBA8BnB,UAAU;IA4BV,IAAI,CAAC,OAAO,GAAG,kBAAkB,EAAE,CAAC;IACpC,IAAI,CAAC,aAAa,GAAG,kBAAkB,EAAE,CAAC;GAC7C;EAEM,MAAM;;IACT,MAAM,SAAS,GAAQ;MACnB,iBAAiB,EAAE,IAAI,CAAC,OAAO;MAC/B,kBAAkB,EAAE,IAAI,CAAC,aAAa;QAClC,CAAC,CAAC,IAAI,CAAC,aAAa;QACpB,CAAC,CAAC,SAAS;MACf,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;KACpD,CAAC;IAEF,wCAAwC;IACxC,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE;MACnD,SAAS,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;KAChE;SAAM,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE;MAC3D,qCAAqC;MACrC,wDAAwD;MACxD,SAAS,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;KACjE;IAED,OAAO,CACH,EAAC,IAAI,kBACD,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,EACxB,KAAK,EAAE;QACH,uBAAuB,EAAE,CAAC,CAAC,CAAA,MAAA,IAAI,CAAC,gBAAgB,0CAAE,IAAI,CAAA;OACzD,IACG,SAAS,IACb,OAAO,EAAE,IAAI,CAAC,OAAO,EACrB,SAAS,EAAE,IAAI,CAAC,SAAS;MAExB,IAAI,CAAC,iBAAiB,EAAE;MACxB,IAAI,CAAC,cAAc,EAAE;MACrB,IAAI,CAAC,UAAU,EAAE;MACjB,IAAI,CAAC,WAAW,EAAE;MAClB,IAAI,CAAC,sBAAsB,EAAE;MAC9B,WAAK,KAAK,EAAC,MAAM;QACZ,IAAI,CAAC,WAAW,EAAE;QAClB,IAAI,CAAC,iBAAiB,EAAE,CACvB;MACL,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CACjC,CACV,CAAC;EACN,CAAC;EA+FD;;;;;;KAMG;EACK,gBAAgB,CACpB,OAAwC;IAExC,IAAI,IAAI,CAAC,eAAe,KAAK,OAAO,EAAE;MAClC,OAAO,IAAI,CAAC,eAAe,CAAC;KAC/B;IACD,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;IAC/B,OAAO,OAAO,CAAC;EACnB,CAAC;EAsGO,gBAAgB;IACpB,OAAO,CACH,IAAI,CAAC,IAAI,KAAK,QAAQ;MACtB,IAAI,CAAC,IAAI,KAAK,OAAO;MACrB,IAAI,CAAC,IAAI,KAAK,UAAU,CAC3B,CAAC;EACN,CAAC;EAEO,WAAW;IACf,QAAQ,IAAI,CAAC,IAAI,EAAE;MACf,KAAK,QAAQ,CAAC,CAAC;QACX,OAAO,QAAQ,CAAC;OACnB;MACD,KAAK,OAAO,CAAC,CAAC;QACV,OAAO,OAAO,CAAC;OAClB;MACD,KAAK,UAAU,CAAC,CAAC;QACb,OAAO,UAAU,CAAC;OACrB;MACD,KAAK,UAAU,CAAC,CAAC;QACb,OAAO,UAAU,CAAC;OACrB;MACD,OAAO,CAAC,CAAC;QACL,OAAO,UAAU,CAAC;OACrB;KACJ;EACL,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BJ","sourcesContent":["import {\n Component,\n Host,\n Prop,\n h,\n Event,\n EventEmitter,\n Element,\n} from '@stencil/core';\nimport { getIconName } from '../icon/get-icon-props';\nimport type { IconSize } from '../icon/icon.types';\nimport { createRandomString } from '../../util/random-string';\nimport { ListItem } from './list-item.types';\nimport { MenuItem } from '../menu/menu.types';\nimport { ListSeparator } from '../../global/shared-types/separator.types';\nimport { CheckboxTemplate } from '../checkbox/checkbox.template';\nimport { RadioButtonTemplate } from '../radio-button-group/radio-button.template';\nimport translate from '../../global/translations';\nimport { Languages } from '../date-picker/date.types';\n\n/**\n * This components displays the list item.\n * This centralizes styles and functionality, and helps reduce redundant code\n * in consumer components such as `limel-list` and `limel-menu-list`.\n *\n * :::note\n * The component has `shadow: false`. There are a few reasons for it:\n * 1. This is to improve performance, and ensure that its internal elements are\n * considered as internal parts of the consumer's DOM.\n * 2. The consumer does not need to implement the interactive styles\n * (such as `visualize-keyboard-focus` mixin) on their own. Since there is no\n * shadow DOM, our mixins can be applied directly to the `limel-list-item` elements,\n * within the component's own styles.\n * 3. Most importantly, the MDCList checks the light DOM of each list item\n * to find native inputs to decide the list mode (checkbox/radio).\n * With `shadow: true`, those inputs would be hidden inside the `limel-list-items`’s\n * shadow DOM, so MDC wouldn’t detect them and therefore throw errors, when given\n * an array index (for the items).\n * With `shadow: false`, the native `<input type=\"checkbox/radio\">` from this template\n * would be visible to MDC.\n * :::\n * @exampleComponent limel-example-list-item-basic\n * @exampleComponent limel-example-list-item-icon\n * @exampleComponent limel-example-list-item-icon-size\n * @exampleComponent limel-example-list-item-pictures\n * @exampleComponent limel-example-list-item-multiple-lines\n * @exampleComponent limel-example-list-item-interactive\n * @exampleComponent limel-example-list-item-radio\n * @exampleComponent limel-example-list-item-checkbox\n * @exampleComponent limel-example-list-item-actions\n * @exampleComponent limel-example-list-item-primary-component\n * @exampleComponent limel-example-list-item-command-text\n * @private\n */\n@Component({\n tag: 'limel-list-item',\n shadow: false,\n styleUrl: 'list-item.scss',\n})\nexport class ListItemComponent implements ListItem {\n /**\n * Defines the language for translations.\n * Will translate the translatable strings on the components.\n */\n @Prop({ reflect: true })\n public language: Languages = 'en';\n\n /**\n * {@inheritdoc ListItem.value}\n */\n @Prop()\n public value?: any;\n\n /**\n * {@inheritdoc ListItem.text}\n */\n @Prop({ reflect: true })\n public text: string;\n\n /**\n * {@inheritdoc ListItem.secondaryText}\n */\n @Prop({ reflect: true })\n public secondaryText?: string;\n\n /**\n * {@inheritdoc ListItem.disabled}\n */\n @Prop({ reflect: true })\n public disabled = false;\n\n /**\n * {@inheritdoc ListItem.icon}\n */\n @Prop()\n public icon?: string | ListItem['icon'];\n\n /**\n * Size of the icon displayed for this item.\n */\n @Prop({ reflect: true })\n public iconSize: IconSize = 'small';\n\n /**\n * Set to `true` if the list should display larger icons with a background\n */\n @Prop({ reflect: true })\n public badgeIcon = false;\n\n /**\n * {@inheritdoc ListItem.selected}\n */\n @Prop({ reflect: true })\n public selected = false;\n\n /**\n * {@inheritdoc ListItem.selected}\n */\n @Prop()\n public actions?: ListItem['actions'];\n\n /**\n * {@inheritdoc ListItem.selected}\n */\n @Prop()\n public primaryComponent?: ListItem['primaryComponent'];\n\n /**\n * {@inheritdoc ListItem.image}\n */\n @Prop()\n public image?: ListItem['image'];\n\n /**\n * The semantic role of the list item. This affects the ARIA role\n * and the interaction behavior.\n *\n * - 'option' → selectable via click/Enter/Space, aria-selected\n * - 'radio'/'checkbox' → selectable, aria-checked\n * - 'menuitem'/'listitem' → activation only, no selection toggle\n */\n @Prop({ reflect: true })\n public type: 'listitem' | 'menuitem' | 'option' | 'radio' | 'checkbox' =\n 'listitem';\n\n /**\n * Emitted when the list item toggles selection (only for selectable types and not disabled).\n */\n @Event()\n public interact: EventEmitter<{\n selected: boolean;\n item: ListItem;\n }>;\n\n @Element()\n private host: HTMLLimelListItemElement;\n\n /**\n * Used to describe the list item for assistive technology.\n */\n private readonly descriptionId: string;\n\n /**\n * Used to label the list item for assistive technology.\n */\n private readonly labelId: string;\n\n // Memoized reference for the action items to avoid unnecessary updates\n private memoizedActions?: Array<MenuItem | ListSeparator>;\n\n constructor() {\n this.labelId = createRandomString();\n this.descriptionId = createRandomString();\n }\n\n public render() {\n const ariaProps: any = {\n 'aria-labelledby': this.labelId,\n 'aria-describedby': this.secondaryText\n ? this.descriptionId\n : undefined,\n 'aria-disabled': this.disabled ? 'true' : 'false',\n };\n\n // ARIA state depending on `role`/`type`\n if (this.type === 'radio' || this.type === 'checkbox') {\n ariaProps['aria-checked'] = this.selected ? 'true' : 'false';\n } else if (this.type === 'option' || this.type === 'menuitem') {\n // aria-selected for `option` (spec);\n // also keep for `menuitem` for visual state consistency\n ariaProps['aria-selected'] = this.selected ? 'true' : 'false';\n }\n\n return (\n <Host\n role={this.getHostRole()}\n class={{\n 'has-primary-component': !!this.primaryComponent?.name,\n }}\n {...ariaProps}\n onClick={this.onClick}\n onKeyDown={this.onKeyDown}\n >\n {this.renderRadioButton()}\n {this.renderCheckbox()}\n {this.renderIcon()}\n {this.renderImage()}\n {this.renderPrimaryComponent()}\n <div class=\"text\">\n {this.renderLabel()}\n {this.renderDescription()}\n </div>\n {this.renderActionMenu(this.actions)}\n </Host>\n );\n }\n\n private renderLabel = () => {\n return (\n <span class=\"label\" id={this.labelId}>\n {this.text}\n </span>\n );\n };\n\n private renderDescription = () => {\n if (!this.secondaryText) {\n return;\n }\n\n return (\n <span class=\"description\" id={this.descriptionId}>\n {this.secondaryText}\n </span>\n );\n };\n\n private renderIcon = () => {\n const iconName = getIconName(this.icon);\n if (!iconName) {\n return;\n }\n\n let iconColor: string | undefined;\n let iconBackgroundColor: string | undefined;\n let title: string | undefined;\n\n if (typeof this.icon === 'object') {\n iconColor = this.icon.color;\n iconBackgroundColor = this.icon.backgroundColor;\n title = this.icon.title;\n }\n\n const iconProps = {\n 'aria-label': title,\n 'aria-hidden': title ? null : 'true',\n name: iconName,\n style: {\n color: iconColor,\n 'background-color': iconBackgroundColor,\n },\n badge: this.badgeIcon,\n size: this.iconSize,\n };\n\n return <limel-icon {...iconProps} />;\n };\n\n private renderPrimaryComponent = () => {\n const primary = this.primaryComponent;\n if (!primary?.name) {\n return;\n }\n\n const PrimaryComponent: any = primary.name;\n const props = primary.props || {};\n\n return <PrimaryComponent {...props} />;\n };\n\n private renderImage = () => {\n if (!this.image) {\n return;\n }\n\n return <img src={this.image.src} alt={this.image.alt} loading=\"lazy\" />;\n };\n\n private renderActionMenu = (actions: Array<MenuItem | ListSeparator>) => {\n if (!actions || actions.length === 0) {\n return;\n }\n\n const stableActions = this.getStableActions(actions);\n return (\n <limel-menu\n class=\"mdc-deprecated-list-item__meta\"\n items={stableActions}\n openDirection=\"left-start\"\n >\n <limel-icon-button\n class=\"action-menu-trigger\"\n slot=\"trigger\"\n icon=\"menu_2\"\n label={this.actionMenuLabel()}\n />\n </limel-menu>\n );\n };\n\n /**\n * Returns a stable reference for the provided actions array to avoid\n * unnecessary re-renders of the action menu when the reference is unchanged.\n *\n * @param actions The actions (and separators) to display in the menu\n * @returns The same array instance that was previously seen, if unchanged\n */\n private getStableActions(\n actions: Array<MenuItem | ListSeparator>\n ): Array<MenuItem | ListSeparator> {\n if (this.memoizedActions === actions) {\n return this.memoizedActions;\n }\n this.memoizedActions = actions;\n return actions;\n }\n\n private renderRadioButton = () => {\n if (this.type !== 'radio') {\n return;\n }\n\n return (\n <RadioButtonTemplate\n id={`radio_${this.labelId}`}\n checked={this.selected}\n disabled={this.disabled}\n />\n );\n };\n\n private renderCheckbox = () => {\n if (this.type !== 'checkbox') {\n return;\n }\n\n return (\n <CheckboxTemplate\n id={`checkbox_${this.labelId}`}\n checked={this.selected}\n disabled={this.disabled}\n />\n );\n };\n\n private onClick = (event: MouseEvent) => {\n if (this.disabled) {\n // Ignore toggling, but don't block embedded controls\n return;\n }\n\n const target = event.target as HTMLElement | null;\n const cameFromActionTrigger = !!target?.closest('.action-menu-trigger');\n const cameFromNoToggle = !!target?.closest('[data-no-toggle]');\n const cameFromMenu = !!target?.closest('limel-menu');\n if (cameFromActionTrigger || cameFromNoToggle || cameFromMenu) {\n return;\n }\n\n if (this.isSelectableType()) {\n this.handleInteraction();\n }\n // For non-selectable types (menuitem/listitem), allow native click to bubble\n };\n\n private onKeyDown = (event: KeyboardEvent) => {\n if (this.disabled) {\n return;\n }\n\n // Only handle keyboard when the host itself has focus.\n // This avoids toggling when Space/Enter is pressed on inner controls\n // like the action menu trigger or any primary component.\n const shadowRoot = this.host.shadowRoot;\n const activeElement = shadowRoot\n ? (shadowRoot.activeElement as HTMLElement | null)\n : null;\n if (activeElement && activeElement !== this.host) {\n return;\n }\n\n const isEnter = event.key === 'Enter';\n const isSpace =\n event.key === ' ' ||\n event.key === 'Space' ||\n event.key === 'Spacebar' ||\n event.code === 'Space';\n\n if (!isEnter && !isSpace) {\n return;\n }\n\n // Avoid re-triggering while key is held down and auto-repeats\n if (event.repeat) {\n // Also prevent default scroll on Space when repeating\n if (isSpace) {\n event.preventDefault();\n }\n return;\n }\n\n // Prevent page scroll and default button behavior on Space\n if (isSpace) {\n event.preventDefault();\n }\n\n if (this.isSelectableType()) {\n this.handleInteraction();\n return;\n }\n\n // For non-selectable items, treat Enter and Space as activation (simulate click)\n if (isEnter || isSpace) {\n this.host.click();\n }\n };\n\n private isSelectableType(): boolean {\n return (\n this.type === 'option' ||\n this.type === 'radio' ||\n this.type === 'checkbox'\n );\n }\n\n private getHostRole(): string {\n switch (this.type) {\n case 'option': {\n return 'option';\n }\n case 'radio': {\n return 'radio';\n }\n case 'checkbox': {\n return 'checkbox';\n }\n case 'menuitem': {\n return 'menuitem';\n }\n default: {\n return 'listitem';\n }\n }\n }\n\n private handleInteraction = () => {\n const newSelected = !this.selected;\n\n const item: ListItem = {\n text: this.text,\n secondaryText: this.secondaryText,\n disabled: this.disabled,\n icon: this.icon,\n selected: newSelected,\n value: this.value,\n actions: this.actions,\n primaryComponent: this.primaryComponent,\n image: this.image,\n };\n\n this.interact.emit({\n selected: newSelected,\n item: item,\n });\n };\n\n private actionMenuLabel = (): string => {\n return translate.get('file-viewer.more-actions', this.language);\n };\n}\n"]}
1
+ {"version":3,"file":"list-item.js","sourceRoot":"","sources":["../../../src/components/list-item/list-item.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component, Host, Prop, h } from '@stencil/core';\nimport { getIconName } from '../icon/get-icon-props';\nimport type { IconSize } from '../icon/icon.types';\nimport { createRandomString } from '../../util/random-string';\nimport { ListItem } from './list-item.types';\nimport { MenuItem } from '../menu/menu.types';\nimport { ListSeparator } from '../../global/shared-types/separator.types';\nimport { CheckboxTemplate } from '../checkbox/checkbox.template';\nimport { RadioButtonTemplate } from '../radio-button-group/radio-button.template';\nimport translate from '../../global/translations';\nimport { Languages } from '../date-picker/date.types';\n\n/**\n * This components displays the list item.\n * This centralizes styles and functionality, and helps reduce redundant code\n * in consumer components such as `limel-list` and `limel-menu-list`.\n *\n * :::note\n * The component has `shadow: false`. There are a few reasons for it:\n * 1. This is to improve performance, and ensure that its internal elements are\n * considered as internal parts of the consumer's DOM.\n * 2. The consumer does not need to implement the interactive styles\n * (such as `visualize-keyboard-focus` mixin) on their own. Since there is no\n * shadow DOM, our mixins can be applied directly to the `limel-list-item` elements,\n * within the component's own styles.\n * 3. Most importantly, the MDCList checks the light DOM of each list item\n * to find native inputs to decide the list mode (checkbox/radio).\n * With `shadow: true`, those inputs would be hidden inside the `limel-list-items`’s\n * shadow DOM, so MDC wouldn’t detect them and therefore throw errors, when given\n * an array index (for the items).\n * With `shadow: false`, the native `<input type=\"checkbox/radio\">` from this template\n * would be visible to MDC.\n * :::\n * @exampleComponent limel-example-list-item-basic\n * @exampleComponent limel-example-list-item-icon\n * @exampleComponent limel-example-list-item-icon-size\n * @exampleComponent limel-example-list-item-pictures\n * @exampleComponent limel-example-list-item-multiple-lines\n * @exampleComponent limel-example-list-item-interactive\n * @exampleComponent limel-example-list-item-radio\n * @exampleComponent limel-example-list-item-checkbox\n * @exampleComponent limel-example-list-item-actions\n * @exampleComponent limel-example-list-item-primary-component\n * @exampleComponent limel-example-list-item-command-text\n * @private\n */\n@Component({\n tag: 'limel-list-item',\n shadow: false,\n styleUrl: 'list-item.scss',\n})\nexport class ListItemComponent implements ListItem {\n /**\n * Defines the language for translations.\n * Will translate the translatable strings on the components.\n */\n @Prop({ reflect: true })\n public language: Languages = 'en';\n\n /**\n * {@inheritdoc ListItem.value}\n */\n @Prop()\n public value?: any;\n\n /**\n * {@inheritdoc ListItem.text}\n */\n @Prop({ reflect: true })\n public text: string;\n\n /**\n * {@inheritdoc ListItem.secondaryText}\n */\n @Prop({ reflect: true })\n public secondaryText?: string;\n\n /**\n * {@inheritdoc ListItem.disabled}\n */\n @Prop({ reflect: true })\n public disabled = false;\n\n /**\n * {@inheritdoc ListItem.icon}\n */\n @Prop()\n public icon?: string | ListItem['icon'];\n\n /**\n * Size of the icon displayed for this item.\n */\n @Prop({ reflect: true })\n public iconSize: IconSize = 'small';\n\n /**\n * Set to `true` if the list should display larger icons with a background\n */\n @Prop({ reflect: true })\n public badgeIcon = false;\n\n /**\n * {@inheritdoc ListItem.selected}\n */\n @Prop({ reflect: true })\n public selected = false;\n\n /**\n * {@inheritdoc ListItem.selected}\n */\n @Prop()\n public actions?: ListItem['actions'];\n\n /**\n * {@inheritdoc ListItem.selected}\n */\n @Prop()\n public primaryComponent?: ListItem['primaryComponent'];\n\n /**\n * {@inheritdoc ListItem.image}\n */\n @Prop()\n public image?: ListItem['image'];\n\n /**\n * The semantic role of the list item. This affects the ARIA role\n * and the interaction behavior.\n *\n * - 'option' → selectable via click/Enter/Space, aria-selected\n * - 'radio'/'checkbox' → selectable, aria-checked\n * - 'menuitem'/'listitem' → activation only, no selection toggle\n */\n @Prop({ reflect: true })\n public type: 'listitem' | 'menuitem' | 'option' | 'radio' | 'checkbox' =\n 'listitem';\n\n /**\n * Used to describe the list item for assistive technology.\n */\n private readonly descriptionId: string;\n\n /**\n * Used to label the list item for assistive technology.\n */\n private readonly labelId: string;\n\n // Memoized reference for the action items to avoid unnecessary updates\n private memoizedActions?: Array<MenuItem | ListSeparator>;\n\n constructor() {\n this.labelId = createRandomString();\n this.descriptionId = createRandomString();\n }\n\n public render() {\n const ariaProps: any = {\n 'aria-labelledby': this.labelId,\n 'aria-describedby': this.secondaryText\n ? this.descriptionId\n : undefined,\n 'aria-disabled': this.disabled ? 'true' : 'false',\n };\n\n // ARIA state depending on `role`/`type`\n if (this.type === 'radio' || this.type === 'checkbox') {\n ariaProps['aria-checked'] = this.selected ? 'true' : 'false';\n } else if (this.type === 'option' || this.type === 'menuitem') {\n // aria-selected for `option` (spec);\n // also keep for `menuitem` for visual state consistency\n ariaProps['aria-selected'] = this.selected ? 'true' : 'false';\n }\n\n return (\n <Host\n role={this.getHostRole()}\n class={{\n 'has-primary-component': !!this.primaryComponent?.name,\n }}\n {...ariaProps}\n >\n {this.renderRadioButton()}\n {this.renderCheckbox()}\n {this.renderIcon()}\n {this.renderImage()}\n {this.renderPrimaryComponent()}\n <div class=\"text\">\n {this.renderLabel()}\n {this.renderDescription()}\n </div>\n {this.renderActionMenu(this.actions)}\n </Host>\n );\n }\n\n private renderLabel = () => {\n return (\n <span class=\"label\" id={this.labelId}>\n {this.text}\n </span>\n );\n };\n\n private renderDescription = () => {\n if (!this.secondaryText) {\n return;\n }\n\n return (\n <span class=\"description\" id={this.descriptionId}>\n {this.secondaryText}\n </span>\n );\n };\n\n private renderIcon = () => {\n const iconName = getIconName(this.icon);\n if (!iconName) {\n return;\n }\n\n let iconColor: string | undefined;\n let iconBackgroundColor: string | undefined;\n let title: string | undefined;\n\n if (typeof this.icon === 'object') {\n iconColor = this.icon.color;\n iconBackgroundColor = this.icon.backgroundColor;\n title = this.icon.title;\n }\n\n const iconProps = {\n 'aria-label': title,\n 'aria-hidden': title ? null : 'true',\n name: iconName,\n style: {\n color: iconColor,\n 'background-color': iconBackgroundColor,\n },\n badge: this.badgeIcon,\n size: this.iconSize,\n };\n\n return <limel-icon {...iconProps} />;\n };\n\n private renderPrimaryComponent = () => {\n const primary = this.primaryComponent;\n if (!primary?.name) {\n return;\n }\n\n const PrimaryComponent: any = primary.name;\n const props = primary.props || {};\n\n return <PrimaryComponent {...props} />;\n };\n\n private renderImage = () => {\n if (!this.image) {\n return;\n }\n\n return <img src={this.image.src} alt={this.image.alt} loading=\"lazy\" />;\n };\n\n private renderActionMenu = (actions: Array<MenuItem | ListSeparator>) => {\n if (!actions || actions.length === 0) {\n return;\n }\n\n const stableActions = this.getStableActions(actions);\n return (\n <limel-menu\n class=\"mdc-deprecated-list-item__meta\"\n items={stableActions}\n openDirection=\"left-start\"\n >\n <limel-icon-button\n class=\"action-menu-trigger\"\n slot=\"trigger\"\n icon=\"menu_2\"\n label={this.actionMenuLabel()}\n />\n </limel-menu>\n );\n };\n\n /**\n * Returns a stable reference for the provided actions array to avoid\n * unnecessary re-renders of the action menu when the reference is unchanged.\n *\n * @param actions The actions (and separators) to display in the menu\n * @returns The same array instance that was previously seen, if unchanged\n */\n private getStableActions(\n actions: Array<MenuItem | ListSeparator>\n ): Array<MenuItem | ListSeparator> {\n if (this.memoizedActions === actions) {\n return this.memoizedActions;\n }\n this.memoizedActions = actions;\n return actions;\n }\n\n private renderRadioButton = () => {\n if (this.type !== 'radio') {\n return;\n }\n\n return (\n <RadioButtonTemplate\n id={`radio_${this.labelId}`}\n checked={this.selected}\n disabled={this.disabled}\n />\n );\n };\n\n private renderCheckbox = () => {\n if (this.type !== 'checkbox') {\n return;\n }\n\n return (\n <CheckboxTemplate\n id={`checkbox_${this.labelId}`}\n checked={this.selected}\n disabled={this.disabled}\n />\n );\n };\n\n private getHostRole(): string {\n switch (this.type) {\n case 'option': {\n return 'option';\n }\n case 'radio': {\n return 'radio';\n }\n case 'checkbox': {\n return 'checkbox';\n }\n case 'menuitem': {\n return 'menuitem';\n }\n default: {\n return 'listitem';\n }\n }\n }\n\n private actionMenuLabel = (): string => {\n return translate.get('file-viewer.more-actions', this.language);\n };\n}\n"]}
@@ -86,9 +86,14 @@ const getLinkDataAtPosition = (view, event) => {
86
86
  return { href: href, text: text, from: from, to: to };
87
87
  };
88
88
  const processModClickEvent = (view, event) => {
89
- const { href } = getLinkDataAtPosition(view, event);
89
+ const linkData = getLinkDataAtPosition(view, event);
90
+ if (!linkData.href) {
91
+ return false;
92
+ }
93
+ event.preventDefault();
94
+ const { href } = linkData;
90
95
  if (href) {
91
- window.open(href, '_blank');
96
+ window.open(href, '_blank', 'noopener,noreferrer');
92
97
  return true;
93
98
  }
94
99
  return false;
@@ -101,32 +106,21 @@ const openLinkMenu = (view, href, text) => {
101
106
  });
102
107
  view.dom.dispatchEvent(event);
103
108
  };
104
- let lastClickTime = 0;
105
- const DOUBLE_CLICK_DELAY = 200;
106
- let clickTimeout;
107
- const processClickEvent = (view, event) => {
108
- const now = Date.now();
109
- if (now - lastClickTime < DOUBLE_CLICK_DELAY) {
110
- clearTimeout(clickTimeout);
111
- lastClickTime = now; // Reset lastClickTime to prevent single-click action
109
+ const processDoubleClickEvent = (view, event) => {
110
+ const linkData = getLinkDataAtPosition(view, event);
111
+ if (!linkData) {
112
112
  return false;
113
113
  }
114
- lastClickTime = now;
115
- clickTimeout = setTimeout(() => {
116
- const linkData = getLinkDataAtPosition(view, event);
117
- if (linkData) {
118
- const { href, text, from, to } = linkData;
119
- const transaction = view.state.tr.setSelection(TextSelection.create(view.state.doc, from, to));
120
- view.dispatch(transaction);
121
- openLinkMenu(view, href, text);
122
- }
123
- }, DOUBLE_CLICK_DELAY);
114
+ const { href, text, from, to } = linkData;
115
+ const transaction = view.state.tr.setSelection(TextSelection.create(view.state.doc, from, to));
116
+ view.dispatch(transaction);
117
+ openLinkMenu(view, href, text);
124
118
  return true;
125
119
  };
126
120
  /**
127
- * Regular expression for matching URLs, mailto links, and phone links
121
+ * Regular expression for matching URLs, mailto links, phone links, and bare www-links
128
122
  */
129
- const URL_REGEX = /(https?:\/\/[^\s<>"']+|mailto:[^\s<>"']+|tel:[^\s<>"']+)/g;
123
+ const URL_REGEX = /(https?:\/\/[^\s<>"']+|mailto:[^\s<>"']+|tel:[^\s<>"']+|www\.[^\s<>"']+)/g;
130
124
  /**
131
125
  * Checks if the text contains any URLs, mailto links, or phone links
132
126
  * @param text
@@ -150,8 +144,19 @@ const createTextNode = (schema, content) => {
150
144
  * @param url
151
145
  */
152
146
  const createLinkNode = (schema, url) => {
153
- const linkMark = schema.marks.link.create(getLinkAttributes(url, url));
154
- return schema.text(url, [linkMark]);
147
+ const normalizeUrlForLinkMark = (input) => {
148
+ let output = input.trim();
149
+ while (output.endsWith('\\')) {
150
+ output = output.slice(0, -1);
151
+ }
152
+ if (output.toLowerCase().startsWith('www.')) {
153
+ output = `https://${output}`;
154
+ }
155
+ return output;
156
+ };
157
+ const normalizedUrl = normalizeUrlForLinkMark(url);
158
+ const linkMark = schema.marks.link.create(getLinkAttributes(normalizedUrl, normalizedUrl));
159
+ return schema.text(normalizedUrl, [linkMark]);
155
160
  };
156
161
  /**
157
162
  * Finds all link matches in the provided text
@@ -171,6 +176,36 @@ const findLinkMatches = (text) => {
171
176
  }
172
177
  return matches;
173
178
  };
179
+ /**
180
+ * Creates nodes for the pasted text while preserving soft line breaks.
181
+ * - Each newline becomes a `hard_break`.
182
+ * - Empty lines are preserved (consecutive newlines => multiple `hard_break`s).
183
+ * - URLs inside each line are converted to link-marked text.
184
+ * @param text - Raw pasted text
185
+ * @param schema - ProseMirror schema
186
+ */
187
+ const createNodesWithLinksAndBreaks = (text, schema) => {
188
+ // Split preserves empty lines between consecutive newlines
189
+ const lines = text.split(/\r\n|\r|\n/);
190
+ const nodes = [];
191
+ for (const [index, line] of lines.entries()) {
192
+ if (line.length > 0) {
193
+ nodes.push(...createNodesWithLinks(line, schema));
194
+ }
195
+ if (index < lines.length - 1) {
196
+ const hb = schema.nodes.hard_break;
197
+ if (hb) {
198
+ nodes.push(hb.create());
199
+ }
200
+ else {
201
+ // Fallback: if schema lacks hard_break, defer to default paste behavior
202
+ // (Do NOT throw; keep behavior stable across versions)
203
+ console.warn('hard_break node not found in schema');
204
+ }
205
+ }
206
+ }
207
+ return nodes;
208
+ };
174
209
  /**
175
210
  * Creates text nodes with links for any URLs, mailto links, or phone links found in the text
176
211
  * @param text
@@ -272,7 +307,8 @@ const processPasteEvent = (view, event) => {
272
307
  if (!text || !hasUrls(text)) {
273
308
  return false;
274
309
  }
275
- const nodes = createNodesWithLinks(text, view.state.schema);
310
+ const nodes = createNodesWithLinksAndBreaks(text, view.state.schema);
311
+ event.preventDefault();
276
312
  pasteAsLink(view, nodes);
277
313
  return true;
278
314
  };
@@ -289,11 +325,12 @@ export const createLinkPlugin = (updateLinkCallback) => {
289
325
  event.button === 0) {
290
326
  return processModClickEvent(view, event);
291
327
  }
328
+ },
329
+ dblclick: (view, event) => {
292
330
  if (event.button !== MouseButtons.Right) {
293
331
  // We want to ignore right-clicks
294
- return processClickEvent(view, event);
332
+ return processDoubleClickEvent(view, event);
295
333
  }
296
- return true;
297
334
  },
298
335
  click: (_view, event) => {
299
336
  if (!(event.target instanceof HTMLElement)) {
@@ -1 +1 @@
1
- {"version":3,"file":"link-plugin.js","sourceRoot":"","sources":["../../../../../../src/components/text-editor/prosemirror-adapter/plugins/link/link-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';\nimport { EditorView } from 'prosemirror-view';\nimport { Mark, Fragment, Node, Schema } from 'prosemirror-model';\nimport { EditorMenuTypes, MouseButtons } from '../../menu/types';\nimport { EditorLink } from '../../../text-editor.types';\nimport { getLinkAttributes } from './utils';\n\nexport const linkPluginKey = new PluginKey('linkPlugin');\n\nexport type UpdateLinkCallback = (text: string, href: string) => void;\n\nconst updateLink = (\n view: EditorView,\n updateLinkCallback?: UpdateLinkCallback\n) => {\n const { from, to } = view.state.selection;\n\n let text = '';\n let href = '';\n view.state.doc.nodesBetween(from, to, (node, pos) => {\n if (node.type.name !== 'text') {\n return;\n }\n\n const fromInNode = Math.max(0, from - pos);\n const toInNode = Math.min(node.text.length, to - pos);\n\n text += node.text.slice(fromInNode, toInNode);\n\n // eslint-disable-next-line unicorn/no-array-for-each\n node.marks.forEach((mark: Mark) => {\n if (mark.type.name === 'link') {\n href = mark.attrs.href;\n }\n });\n });\n\n if (updateLinkCallback) {\n updateLinkCallback(text, href);\n }\n};\n\n/**\n * Finds the start position of the link node ensuring the href matches the original link's href.\n * @param doc - The ProseMirror document.\n * @param pos - The position to start searching from.\n * @param href - The href attribute of the original link mark.\n * @returns The start position of the link node.\n */\nconst findStart = (doc, pos, href) => {\n while (pos > 0) {\n const node = doc.nodeAt(pos - 1);\n if (\n !node?.isText ||\n !node.marks.some(\n (mark: Mark) =>\n mark.type.name === EditorMenuTypes.Link &&\n mark.attrs.href === href\n )\n ) {\n break;\n }\n\n pos--;\n }\n\n return pos;\n};\n\n/**\n * Finds the end position of the link node ensuring the href matches the original link's href.\n * @param doc - The ProseMirror document.\n * @param pos - The position to start searching from.\n * @param href - The href attribute of the original link mark.\n * @returns The end position of the link node.\n */\nconst findEnd = (doc, pos, href) => {\n while (pos < doc.content.size) {\n const node = doc.nodeAt(pos);\n if (\n !node?.isText ||\n !node.marks.some(\n (mark) =>\n mark.type.name === EditorMenuTypes.Link &&\n mark.attrs.href === href\n )\n ) {\n break;\n }\n\n pos++;\n }\n\n return pos;\n};\n\n/**\n * Gets the link data at the specified position.\n * @param view - The ProseMirror editor view.\n * @param event - The mouse event.\n * @returns An object containing the link data or null if no link is found.\n */\nconst getLinkDataAtPosition = (view: EditorView, event: MouseEvent) => {\n const pos = view.posAtCoords({ left: event.clientX, top: event.clientY });\n const node = view.state.doc.nodeAt(pos?.pos);\n if (!node) {\n return null;\n }\n\n const linkMark = node.marks.find(\n (mark) => mark.type.name === EditorMenuTypes.Link\n );\n if (!linkMark) {\n return null;\n }\n\n const href = linkMark.attrs.href;\n const from = findStart(view.state.doc, pos.pos, href);\n const to = findEnd(view.state.doc, pos.pos, href);\n const text = view.state.doc.textBetween(from, to, ' ');\n\n return { href: href, text: text, from: from, to: to };\n};\n\nconst processModClickEvent = (view: EditorView, event: MouseEvent): boolean => {\n const { href } = getLinkDataAtPosition(view, event);\n if (href) {\n window.open(href, '_blank');\n\n return true;\n }\n\n return false;\n};\n\nconst openLinkMenu = (view: EditorView, href: string, text: string) => {\n const event = new CustomEvent<EditorLink>('open-editor-link-menu', {\n detail: { href: href, text: text },\n bubbles: true,\n composed: true,\n });\n view.dom.dispatchEvent(event);\n};\n\nlet lastClickTime = 0;\nconst DOUBLE_CLICK_DELAY = 200;\nlet clickTimeout;\n\nconst processClickEvent = (view: EditorView, event: MouseEvent): boolean => {\n const now = Date.now();\n\n if (now - lastClickTime < DOUBLE_CLICK_DELAY) {\n clearTimeout(clickTimeout);\n lastClickTime = now; // Reset lastClickTime to prevent single-click action\n\n return false;\n }\n\n lastClickTime = now;\n\n clickTimeout = setTimeout(() => {\n const linkData = getLinkDataAtPosition(view, event);\n if (linkData) {\n const { href, text, from, to } = linkData;\n const transaction = view.state.tr.setSelection(\n TextSelection.create(view.state.doc, from, to)\n );\n view.dispatch(transaction);\n openLinkMenu(view, href, text);\n }\n }, DOUBLE_CLICK_DELAY);\n\n return true;\n};\n\n/**\n * Regular expression for matching URLs, mailto links, and phone links\n */\nconst URL_REGEX = /(https?:\\/\\/[^\\s<>\"']+|mailto:[^\\s<>\"']+|tel:[^\\s<>\"']+)/g;\n\n/**\n * Checks if the text contains any URLs, mailto links, or phone links\n * @param text\n */\nconst hasUrls = (text: string): boolean => {\n // Reset regex before use\n URL_REGEX.lastIndex = 0;\n\n return URL_REGEX.test(text);\n};\n\n/**\n * Creates a text node with the provided content\n * @param schema\n * @param content\n */\nconst createTextNode = (schema: Schema, content: string): Node => {\n return schema.text(content);\n};\n\n/**\n * Creates a link node with the provided URL\n * @param schema\n * @param url\n */\nconst createLinkNode = (schema: Schema, url: string): Node => {\n const linkMark = schema.marks.link.create(getLinkAttributes(url, url));\n\n return schema.text(url, [linkMark]);\n};\n\n/**\n * Finds all link matches in the provided text\n * @param text\n */\nconst findLinkMatches = (\n text: string\n): Array<{ url: string; start: number; end: number }> => {\n const matches = [];\n let match: RegExpExecArray | null;\n\n // Reset regex before use\n URL_REGEX.lastIndex = 0;\n\n while ((match = URL_REGEX.exec(text)) !== null) {\n matches.push({\n url: match[0],\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n\n return matches;\n};\n\n/**\n * Creates text nodes with links for any URLs, mailto links, or phone links found in the text\n * @param text\n * @param schema\n */\nconst createNodesWithLinks = (text: string, schema: Schema): Node[] => {\n const nodes: Node[] = [];\n const matches = findLinkMatches(text);\n\n if (matches.length === 0) {\n // No links found, just return the text as a single node\n return [createTextNode(schema, text)];\n }\n\n let lastIndex = 0;\n\n // Process each match\n for (const match of matches) {\n // Add text before the current link if any\n if (match.start > lastIndex) {\n nodes.push(\n createTextNode(schema, text.slice(lastIndex, match.start))\n );\n }\n\n // Add the link node\n nodes.push(createLinkNode(schema, match.url));\n\n lastIndex = match.end;\n }\n\n // Add any remaining text after the last link\n if (lastIndex < text.length) {\n nodes.push(createTextNode(schema, text.slice(lastIndex)));\n }\n\n return nodes;\n};\n\n/**\n * Pastes nodes at the current selection\n * @param view - The editor view\n * @param nodes - Array of nodes to paste\n */\nconst pasteAsLink = (view: EditorView, nodes: Node[]) => {\n if (nodes.length === 0) {\n return;\n }\n\n if (isSingleLinkNode(nodes)) {\n insertSingleLink(view, nodes[0]);\n } else {\n insertNodeFragment(view, nodes);\n }\n};\n\n/**\n * Checks if the nodes array contains just a single link node\n * @param nodes\n */\nconst isSingleLinkNode = (nodes: Node[]): boolean => {\n if (nodes.length !== 1) {\n return false;\n }\n\n const node = nodes[0];\n\n // Must be text with non-empty content\n if (!node.isText || !node.text || node.text.trim() === '') {\n return false;\n }\n\n // Must have a link mark (even if there are other marks, we just care about link presence)\n return node.marks.some((mark) => mark.type.name === 'link');\n};\n\n/**\n * Inserts a single link node, applying it to selected text if present\n * @param view\n * @param linkNode\n */\nconst insertSingleLink = (view: EditorView, linkNode: Node) => {\n const { state, dispatch } = view;\n const { from, to } = state.selection;\n\n const linkMark = linkNode.marks.find((mark) => mark.type.name === 'link');\n\n // Use selected text if there's a selection, otherwise use the URL\n const selectedText =\n state.doc.textBetween(from, to, ' ') || linkMark.attrs.href;\n\n // Insert the text and add the link mark\n dispatch(\n state.tr\n .insertText(selectedText, from, to)\n .addMark(from, from + selectedText.length, linkMark)\n );\n};\n\n/**\n * Inserts multiple nodes as a fragment at the current selection\n * @param view - The editor view\n * @param nodes - Array of nodes to insert\n */\nconst insertNodeFragment = (view: EditorView, nodes: Node[]) => {\n const { state, dispatch } = view;\n const { from, to } = state.selection;\n\n // Create a fragment from the array of nodes\n const fragment = Fragment.fromArray(nodes);\n\n // Replace the current selection with the fragment\n dispatch(state.tr.replaceWith(from, to, fragment));\n};\n\n/**\n * Handles pasted content, converting URLs to links\n * @param view\n * @param event\n */\nconst processPasteEvent = (\n view: EditorView,\n event: ClipboardEvent\n): boolean => {\n const text = event.clipboardData?.getData('text/plain');\n if (!text || !hasUrls(text)) {\n return false;\n }\n\n const nodes = createNodesWithLinks(text, view.state.schema);\n pasteAsLink(view, nodes);\n\n return true;\n};\n\nexport const createLinkPlugin = (updateLinkCallback?: UpdateLinkCallback) => {\n return new Plugin({\n key: linkPluginKey,\n props: {\n handlePaste: (view, event) => {\n return processPasteEvent(view, event);\n },\n handleDOMEvents: {\n mousedown: (view, event) => {\n if (\n (event.metaKey || event.ctrlKey) &&\n event.button === 0\n ) {\n return processModClickEvent(view, event);\n }\n\n if (event.button !== MouseButtons.Right) {\n // We want to ignore right-clicks\n return processClickEvent(view, event);\n }\n\n return true;\n },\n click: (_view, event) => {\n if (!(event.target instanceof HTMLElement)) {\n return;\n }\n\n // Prevent unhandled navigation and bubbling for link clicks\n const link = event.target.closest('a');\n if (link) {\n event.preventDefault();\n event.stopPropagation();\n }\n },\n },\n },\n view: () => ({\n update: (view) => {\n updateLink(view, updateLinkCallback);\n },\n }),\n });\n};\n"]}
1
+ {"version":3,"file":"link-plugin.js","sourceRoot":"","sources":["../../../../../../src/components/text-editor/prosemirror-adapter/plugins/link/link-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';\nimport { EditorView } from 'prosemirror-view';\nimport { Mark, Fragment, Node, Schema } from 'prosemirror-model';\nimport { EditorMenuTypes, MouseButtons } from '../../menu/types';\nimport { EditorLink } from '../../../text-editor.types';\nimport { getLinkAttributes } from './utils';\n\nexport const linkPluginKey = new PluginKey('linkPlugin');\n\nexport type UpdateLinkCallback = (text: string, href: string) => void;\n\nconst updateLink = (\n view: EditorView,\n updateLinkCallback?: UpdateLinkCallback\n) => {\n const { from, to } = view.state.selection;\n\n let text = '';\n let href = '';\n view.state.doc.nodesBetween(from, to, (node, pos) => {\n if (node.type.name !== 'text') {\n return;\n }\n\n const fromInNode = Math.max(0, from - pos);\n const toInNode = Math.min(node.text.length, to - pos);\n\n text += node.text.slice(fromInNode, toInNode);\n\n // eslint-disable-next-line unicorn/no-array-for-each\n node.marks.forEach((mark: Mark) => {\n if (mark.type.name === 'link') {\n href = mark.attrs.href;\n }\n });\n });\n\n if (updateLinkCallback) {\n updateLinkCallback(text, href);\n }\n};\n\n/**\n * Finds the start position of the link node ensuring the href matches the original link's href.\n * @param doc - The ProseMirror document.\n * @param pos - The position to start searching from.\n * @param href - The href attribute of the original link mark.\n * @returns The start position of the link node.\n */\nconst findStart = (doc, pos, href) => {\n while (pos > 0) {\n const node = doc.nodeAt(pos - 1);\n if (\n !node?.isText ||\n !node.marks.some(\n (mark: Mark) =>\n mark.type.name === EditorMenuTypes.Link &&\n mark.attrs.href === href\n )\n ) {\n break;\n }\n\n pos--;\n }\n\n return pos;\n};\n\n/**\n * Finds the end position of the link node ensuring the href matches the original link's href.\n * @param doc - The ProseMirror document.\n * @param pos - The position to start searching from.\n * @param href - The href attribute of the original link mark.\n * @returns The end position of the link node.\n */\nconst findEnd = (doc, pos, href) => {\n while (pos < doc.content.size) {\n const node = doc.nodeAt(pos);\n if (\n !node?.isText ||\n !node.marks.some(\n (mark) =>\n mark.type.name === EditorMenuTypes.Link &&\n mark.attrs.href === href\n )\n ) {\n break;\n }\n\n pos++;\n }\n\n return pos;\n};\n\n/**\n * Gets the link data at the specified position.\n * @param view - The ProseMirror editor view.\n * @param event - The mouse event.\n * @returns An object containing the link data or null if no link is found.\n */\nconst getLinkDataAtPosition = (view: EditorView, event: MouseEvent) => {\n const pos = view.posAtCoords({ left: event.clientX, top: event.clientY });\n const node = view.state.doc.nodeAt(pos?.pos);\n if (!node) {\n return null;\n }\n\n const linkMark = node.marks.find(\n (mark) => mark.type.name === EditorMenuTypes.Link\n );\n if (!linkMark) {\n return null;\n }\n\n const href = linkMark.attrs.href;\n const from = findStart(view.state.doc, pos.pos, href);\n const to = findEnd(view.state.doc, pos.pos, href);\n const text = view.state.doc.textBetween(from, to, ' ');\n\n return { href: href, text: text, from: from, to: to };\n};\n\nconst processModClickEvent = (view: EditorView, event: MouseEvent): boolean => {\n const linkData = getLinkDataAtPosition(view, event);\n if (!linkData.href) {\n return false;\n }\n event.preventDefault();\n\n const { href } = linkData;\n if (href) {\n window.open(href, '_blank', 'noopener,noreferrer');\n\n return true;\n }\n\n return false;\n};\n\nconst openLinkMenu = (view: EditorView, href: string, text: string) => {\n const event = new CustomEvent<EditorLink>('open-editor-link-menu', {\n detail: { href: href, text: text },\n bubbles: true,\n composed: true,\n });\n view.dom.dispatchEvent(event);\n};\n\nconst processDoubleClickEvent = (\n view: EditorView,\n event: MouseEvent\n): boolean => {\n const linkData = getLinkDataAtPosition(view, event);\n if (!linkData) {\n return false;\n }\n\n const { href, text, from, to } = linkData;\n const transaction = view.state.tr.setSelection(\n TextSelection.create(view.state.doc, from, to)\n );\n view.dispatch(transaction);\n openLinkMenu(view, href, text);\n\n return true;\n};\n\n/**\n * Regular expression for matching URLs, mailto links, phone links, and bare www-links\n */\nconst URL_REGEX =\n /(https?:\\/\\/[^\\s<>\"']+|mailto:[^\\s<>\"']+|tel:[^\\s<>\"']+|www\\.[^\\s<>\"']+)/g;\n\n/**\n * Checks if the text contains any URLs, mailto links, or phone links\n * @param text\n */\nconst hasUrls = (text: string): boolean => {\n // Reset regex before use\n URL_REGEX.lastIndex = 0;\n\n return URL_REGEX.test(text);\n};\n\n/**\n * Creates a text node with the provided content\n * @param schema\n * @param content\n */\nconst createTextNode = (schema: Schema, content: string): Node => {\n return schema.text(content);\n};\n\n/**\n * Creates a link node with the provided URL\n * @param schema\n * @param url\n */\nconst createLinkNode = (schema: Schema, url: string): Node => {\n const normalizeUrlForLinkMark = (input: string): string => {\n let output = input.trim();\n while (output.endsWith('\\\\')) {\n output = output.slice(0, -1);\n }\n if (output.toLowerCase().startsWith('www.')) {\n output = `https://${output}`;\n }\n return output;\n };\n\n const normalizedUrl = normalizeUrlForLinkMark(url);\n const linkMark = schema.marks.link.create(\n getLinkAttributes(normalizedUrl, normalizedUrl)\n );\n\n return schema.text(normalizedUrl, [linkMark]);\n};\n\n/**\n * Finds all link matches in the provided text\n * @param text\n */\nconst findLinkMatches = (\n text: string\n): Array<{ url: string; start: number; end: number }> => {\n const matches = [];\n let match: RegExpExecArray | null;\n\n // Reset regex before use\n URL_REGEX.lastIndex = 0;\n\n while ((match = URL_REGEX.exec(text)) !== null) {\n matches.push({\n url: match[0],\n start: match.index,\n end: match.index + match[0].length,\n });\n }\n\n return matches;\n};\n\n/**\n * Creates nodes for the pasted text while preserving soft line breaks.\n * - Each newline becomes a `hard_break`.\n * - Empty lines are preserved (consecutive newlines => multiple `hard_break`s).\n * - URLs inside each line are converted to link-marked text.\n * @param text - Raw pasted text\n * @param schema - ProseMirror schema\n */\nconst createNodesWithLinksAndBreaks = (\n text: string,\n schema: Schema\n): Node[] => {\n // Split preserves empty lines between consecutive newlines\n const lines = text.split(/\\r\\n|\\r|\\n/);\n const nodes: Node[] = [];\n\n for (const [index, line] of lines.entries()) {\n if (line.length > 0) {\n nodes.push(...createNodesWithLinks(line, schema));\n }\n if (index < lines.length - 1) {\n const hb = schema.nodes.hard_break;\n if (hb) {\n nodes.push(hb.create());\n } else {\n // Fallback: if schema lacks hard_break, defer to default paste behavior\n // (Do NOT throw; keep behavior stable across versions)\n console.warn('hard_break node not found in schema');\n }\n }\n }\n return nodes;\n};\n\n/**\n * Creates text nodes with links for any URLs, mailto links, or phone links found in the text\n * @param text\n * @param schema\n */\nconst createNodesWithLinks = (text: string, schema: Schema): Node[] => {\n const nodes: Node[] = [];\n const matches = findLinkMatches(text);\n\n if (matches.length === 0) {\n // No links found, just return the text as a single node\n return [createTextNode(schema, text)];\n }\n\n let lastIndex = 0;\n\n // Process each match\n for (const match of matches) {\n // Add text before the current link if any\n if (match.start > lastIndex) {\n nodes.push(\n createTextNode(schema, text.slice(lastIndex, match.start))\n );\n }\n\n // Add the link node\n nodes.push(createLinkNode(schema, match.url));\n\n lastIndex = match.end;\n }\n\n // Add any remaining text after the last link\n if (lastIndex < text.length) {\n nodes.push(createTextNode(schema, text.slice(lastIndex)));\n }\n\n return nodes;\n};\n\n/**\n * Pastes nodes at the current selection\n * @param view - The editor view\n * @param nodes - Array of nodes to paste\n */\nconst pasteAsLink = (view: EditorView, nodes: Node[]) => {\n if (nodes.length === 0) {\n return;\n }\n\n if (isSingleLinkNode(nodes)) {\n insertSingleLink(view, nodes[0]);\n } else {\n insertNodeFragment(view, nodes);\n }\n};\n\n/**\n * Checks if the nodes array contains just a single link node\n * @param nodes\n */\nconst isSingleLinkNode = (nodes: Node[]): boolean => {\n if (nodes.length !== 1) {\n return false;\n }\n\n const node = nodes[0];\n\n // Must be text with non-empty content\n if (!node.isText || !node.text || node.text.trim() === '') {\n return false;\n }\n\n // Must have a link mark (even if there are other marks, we just care about link presence)\n return node.marks.some((mark) => mark.type.name === 'link');\n};\n\n/**\n * Inserts a single link node, applying it to selected text if present\n * @param view\n * @param linkNode\n */\nconst insertSingleLink = (view: EditorView, linkNode: Node) => {\n const { state, dispatch } = view;\n const { from, to } = state.selection;\n\n const linkMark = linkNode.marks.find((mark) => mark.type.name === 'link');\n\n // Use selected text if there's a selection, otherwise use the URL\n const selectedText =\n state.doc.textBetween(from, to, ' ') || linkMark.attrs.href;\n\n // Insert the text and add the link mark\n dispatch(\n state.tr\n .insertText(selectedText, from, to)\n .addMark(from, from + selectedText.length, linkMark)\n );\n};\n\n/**\n * Inserts multiple nodes as a fragment at the current selection\n * @param view - The editor view\n * @param nodes - Array of nodes to insert\n */\nconst insertNodeFragment = (view: EditorView, nodes: Node[]) => {\n const { state, dispatch } = view;\n const { from, to } = state.selection;\n\n // Create a fragment from the array of nodes\n const fragment = Fragment.fromArray(nodes);\n\n // Replace the current selection with the fragment\n dispatch(state.tr.replaceWith(from, to, fragment));\n};\n\n/**\n * Handles pasted content, converting URLs to links\n * @param view\n * @param event\n */\nconst processPasteEvent = (\n view: EditorView,\n event: ClipboardEvent\n): boolean => {\n const text = event.clipboardData?.getData('text/plain');\n\n if (!text || !hasUrls(text)) {\n return false;\n }\n\n const nodes = createNodesWithLinksAndBreaks(text, view.state.schema);\n\n event.preventDefault();\n pasteAsLink(view, nodes);\n\n return true;\n};\n\nexport const createLinkPlugin = (updateLinkCallback?: UpdateLinkCallback) => {\n return new Plugin({\n key: linkPluginKey,\n props: {\n handlePaste: (view, event) => {\n return processPasteEvent(view, event);\n },\n handleDOMEvents: {\n mousedown: (view, event) => {\n if (\n (event.metaKey || event.ctrlKey) &&\n event.button === 0\n ) {\n return processModClickEvent(view, event);\n }\n },\n dblclick: (view, event) => {\n if (event.button !== MouseButtons.Right) {\n // We want to ignore right-clicks\n return processDoubleClickEvent(view, event);\n }\n },\n click: (_view, event) => {\n if (!(event.target instanceof HTMLElement)) {\n return;\n }\n\n // Prevent unhandled navigation and bubbling for link clicks\n const link = event.target.closest('a');\n if (link) {\n event.preventDefault();\n event.stopPropagation();\n }\n },\n },\n },\n view: () => ({\n update: (view) => {\n updateLink(view, updateLinkCallback);\n },\n }),\n });\n};\n"]}
@@ -1,4 +1,4 @@
1
- import { h, r as registerInstance, c as createEvent, H as Host, g as getElement } from './index-2714248e.js';
1
+ import { h, r as registerInstance, H as Host } from './index-2714248e.js';
2
2
  import { g as getIconName } from './get-icon-props-37514418.js';
3
3
  import { c as createRandomString } from './random-string-355331d3.js';
4
4
  import { C as CheckboxTemplate } from './checkbox.template-9acc6347.js';
@@ -21,7 +21,6 @@ const listItemCss = "@charset \"UTF-8\";*,*:before,*:after{box-sizing:border-box
21
21
  const ListItemComponent = class {
22
22
  constructor(hostRef) {
23
23
  registerInstance(this, hostRef);
24
- this.interact = createEvent(this, "interact", 7);
25
24
  this.renderLabel = () => {
26
25
  return (h("span", { class: "label", id: this.labelId }, this.text));
27
26
  };
@@ -91,84 +90,6 @@ const ListItemComponent = class {
91
90
  }
92
91
  return (h(CheckboxTemplate, { id: `checkbox_${this.labelId}`, checked: this.selected, disabled: this.disabled }));
93
92
  };
94
- this.onClick = (event) => {
95
- if (this.disabled) {
96
- // Ignore toggling, but don't block embedded controls
97
- return;
98
- }
99
- const target = event.target;
100
- const cameFromActionTrigger = !!(target === null || target === void 0 ? void 0 : target.closest('.action-menu-trigger'));
101
- const cameFromNoToggle = !!(target === null || target === void 0 ? void 0 : target.closest('[data-no-toggle]'));
102
- const cameFromMenu = !!(target === null || target === void 0 ? void 0 : target.closest('limel-menu'));
103
- if (cameFromActionTrigger || cameFromNoToggle || cameFromMenu) {
104
- return;
105
- }
106
- if (this.isSelectableType()) {
107
- this.handleInteraction();
108
- }
109
- // For non-selectable types (menuitem/listitem), allow native click to bubble
110
- };
111
- this.onKeyDown = (event) => {
112
- if (this.disabled) {
113
- return;
114
- }
115
- // Only handle keyboard when the host itself has focus.
116
- // This avoids toggling when Space/Enter is pressed on inner controls
117
- // like the action menu trigger or any primary component.
118
- const shadowRoot = this.host.shadowRoot;
119
- const activeElement = shadowRoot
120
- ? shadowRoot.activeElement
121
- : null;
122
- if (activeElement && activeElement !== this.host) {
123
- return;
124
- }
125
- const isEnter = event.key === 'Enter';
126
- const isSpace = event.key === ' ' ||
127
- event.key === 'Space' ||
128
- event.key === 'Spacebar' ||
129
- event.code === 'Space';
130
- if (!isEnter && !isSpace) {
131
- return;
132
- }
133
- // Avoid re-triggering while key is held down and auto-repeats
134
- if (event.repeat) {
135
- // Also prevent default scroll on Space when repeating
136
- if (isSpace) {
137
- event.preventDefault();
138
- }
139
- return;
140
- }
141
- // Prevent page scroll and default button behavior on Space
142
- if (isSpace) {
143
- event.preventDefault();
144
- }
145
- if (this.isSelectableType()) {
146
- this.handleInteraction();
147
- return;
148
- }
149
- // For non-selectable items, treat Enter and Space as activation (simulate click)
150
- if (isEnter || isSpace) {
151
- this.host.click();
152
- }
153
- };
154
- this.handleInteraction = () => {
155
- const newSelected = !this.selected;
156
- const item = {
157
- text: this.text,
158
- secondaryText: this.secondaryText,
159
- disabled: this.disabled,
160
- icon: this.icon,
161
- selected: newSelected,
162
- value: this.value,
163
- actions: this.actions,
164
- primaryComponent: this.primaryComponent,
165
- image: this.image,
166
- };
167
- this.interact.emit({
168
- selected: newSelected,
169
- item: item,
170
- });
171
- };
172
93
  this.actionMenuLabel = () => {
173
94
  return translate.get('file-viewer.more-actions', this.language);
174
95
  };
@@ -208,7 +129,7 @@ const ListItemComponent = class {
208
129
  }
209
130
  return (h(Host, Object.assign({ role: this.getHostRole(), class: {
210
131
  'has-primary-component': !!((_a = this.primaryComponent) === null || _a === void 0 ? void 0 : _a.name),
211
- } }, ariaProps, { onClick: this.onClick, onKeyDown: this.onKeyDown }), this.renderRadioButton(), this.renderCheckbox(), this.renderIcon(), this.renderImage(), this.renderPrimaryComponent(), h("div", { class: "text" }, this.renderLabel(), this.renderDescription()), this.renderActionMenu(this.actions)));
132
+ } }, ariaProps), this.renderRadioButton(), this.renderCheckbox(), this.renderIcon(), this.renderImage(), this.renderPrimaryComponent(), h("div", { class: "text" }, this.renderLabel(), this.renderDescription()), this.renderActionMenu(this.actions)));
212
133
  }
213
134
  /**
214
135
  * Returns a stable reference for the provided actions array to avoid
@@ -224,11 +145,6 @@ const ListItemComponent = class {
224
145
  this.memoizedActions = actions;
225
146
  return actions;
226
147
  }
227
- isSelectableType() {
228
- return (this.type === 'option' ||
229
- this.type === 'radio' ||
230
- this.type === 'checkbox');
231
- }
232
148
  getHostRole() {
233
149
  switch (this.type) {
234
150
  case 'option': {
@@ -248,7 +164,6 @@ const ListItemComponent = class {
248
164
  }
249
165
  }
250
166
  }
251
- get host() { return getElement(this); }
252
167
  };
253
168
  ListItemComponent.style = listItemCss;
254
169