mn-angular-lib 1.0.75 → 1.0.76

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.
@@ -256,41 +256,76 @@ const mnButtonVariants = tv({
256
256
  wrap: {
257
257
  true: 'whitespace-normal',
258
258
  false: 'whitespace-nowrap',
259
- }
259
+ },
260
+ hover: {
261
+ true: '',
262
+ },
260
263
  },
261
264
  compoundVariants: [
262
- // Fill
263
- { variant: 'fill', color: 'primary', class: 'bg-primary text-primary-content border border-primary hover:brightness-60' },
264
- { variant: 'fill', color: 'secondary', class: 'bg-neutral text-neutral-content border border-neutral hover:brightness-60' },
265
- { variant: 'fill', color: 'danger', class: 'bg-error text-error-content border border-error hover:brightness-60' },
266
- { variant: 'fill', color: 'warning', class: 'bg-warning text-warning-content border border-warning hover:brightness-60' },
267
- { variant: 'fill', color: 'success', class: 'bg-success text-success-content border border-success hover:brightness-60' },
268
- { variant: 'fill', color: 'accent', class: 'bg-accent text-accent-content border border-accent hover:brightness-60' },
269
- { variant: 'fill', color: 'gray', class: 'bg-base-content/10 text-base-content/70 border border-base-content/10 hover:bg-base-content/20' },
270
- // Outline
271
- { variant: 'outline', color: 'primary', class: 'border-primary text-primary hover:bg-primary/10' },
272
- { variant: 'outline', color: 'secondary', class: 'border-neutral text-neutral hover:bg-neutral/10' },
273
- { variant: 'outline', color: 'danger', class: 'border-error text-error hover:bg-error/10' },
274
- { variant: 'outline', color: 'warning', class: 'border-warning text-warning hover:bg-warning/10' },
275
- { variant: 'outline', color: 'success', class: 'border-success text-success hover:bg-success/10' },
276
- { variant: 'outline', color: 'accent', class: 'border-accent text-accent hover:bg-accent/10' },
277
- { variant: 'outline', color: 'gray', class: 'border-base-content/70 text-base-content/70 hover:bg-base-content/10' },
278
- // Text
279
- { variant: 'text', color: 'primary', class: 'text-primary hover:bg-primary/10' },
280
- { variant: 'text', color: 'secondary', class: 'text-neutral hover:bg-neutral/10' },
281
- { variant: 'text', color: 'danger', class: 'text-error hover:bg-error/10' },
282
- { variant: 'text', color: 'warning', class: 'text-warning hover:bg-warning/10' },
283
- { variant: 'text', color: 'success', class: 'text-success hover:bg-success/10' },
284
- { variant: 'text', color: 'accent', class: 'text-accent hover:bg-accent/10' },
285
- { variant: 'text', color: 'gray', class: 'text-base-content/70 hover:bg-base-content/10' },
286
- // Text Underline
287
- { variant: 'textUnderline', color: 'primary', class: 'text-primary underline underline-offset-2 hover:bg-primary/10' },
288
- { variant: 'textUnderline', color: 'secondary', class: 'text-neutral underline underline-offset-2 hover:bg-neutral/10' },
289
- { variant: 'textUnderline', color: 'danger', class: 'text-error underline underline-offset-2 hover:bg-error/10' },
290
- { variant: 'textUnderline', color: 'warning', class: 'text-warning underline underline-offset-2 hover:bg-warning/10' },
291
- { variant: 'textUnderline', color: 'success', class: 'text-success underline underline-offset-2 hover:bg-success/10' },
292
- { variant: 'textUnderline', color: 'accent', class: 'text-accent underline underline-offset-2 hover:bg-accent/10' },
293
- { variant: 'textUnderline', color: 'gray', class: 'text-base-content/70 underline underline-offset-2 hover:bg-base-content/10' },
265
+ // Fill — base
266
+ { variant: 'fill', color: 'primary', class: 'bg-primary text-primary-content border border-primary' },
267
+ { variant: 'fill', color: 'secondary', class: 'bg-neutral text-neutral-content border border-neutral' },
268
+ { variant: 'fill', color: 'danger', class: 'bg-error text-error-content border border-error' },
269
+ { variant: 'fill', color: 'warning', class: 'bg-warning text-warning-content border border-warning' },
270
+ { variant: 'fill', color: 'success', class: 'bg-success text-success-content border border-success' },
271
+ { variant: 'fill', color: 'accent', class: 'bg-accent text-accent-content border border-accent' },
272
+ { variant: 'fill', color: 'gray', class: 'bg-base-content/10 text-base-content/70 border border-base-content/10' },
273
+ // Fill — hover
274
+ { variant: 'fill', color: 'primary', hover: true, class: 'hover:brightness-60' },
275
+ { variant: 'fill', color: 'secondary', hover: true, class: 'hover:brightness-60' },
276
+ { variant: 'fill', color: 'danger', hover: true, class: 'hover:brightness-60' },
277
+ { variant: 'fill', color: 'warning', hover: true, class: 'hover:brightness-60' },
278
+ { variant: 'fill', color: 'success', hover: true, class: 'hover:brightness-60' },
279
+ { variant: 'fill', color: 'accent', hover: true, class: 'hover:brightness-60' },
280
+ { variant: 'fill', color: 'gray', hover: true, class: 'hover:bg-base-content/20' },
281
+ // Outline — base
282
+ { variant: 'outline', color: 'primary', class: 'border-primary text-primary' },
283
+ { variant: 'outline', color: 'secondary', class: 'border-neutral text-neutral' },
284
+ { variant: 'outline', color: 'danger', class: 'border-error text-error' },
285
+ { variant: 'outline', color: 'warning', class: 'border-warning text-warning' },
286
+ { variant: 'outline', color: 'success', class: 'border-success text-success' },
287
+ { variant: 'outline', color: 'accent', class: 'border-accent text-accent' },
288
+ { variant: 'outline', color: 'gray', class: 'border-base-content/70 text-base-content/70' },
289
+ // Outline — hover
290
+ { variant: 'outline', color: 'primary', hover: true, class: 'hover:bg-primary/10' },
291
+ { variant: 'outline', color: 'secondary', hover: true, class: 'hover:bg-neutral/10' },
292
+ { variant: 'outline', color: 'danger', hover: true, class: 'hover:bg-error/10' },
293
+ { variant: 'outline', color: 'warning', hover: true, class: 'hover:bg-warning/10' },
294
+ { variant: 'outline', color: 'success', hover: true, class: 'hover:bg-success/10' },
295
+ { variant: 'outline', color: 'accent', hover: true, class: 'hover:bg-accent/10' },
296
+ { variant: 'outline', color: 'gray', hover: true, class: 'hover:bg-base-content/10' },
297
+ // Text — base
298
+ { variant: 'text', color: 'primary', class: 'text-primary' },
299
+ { variant: 'text', color: 'secondary', class: 'text-neutral' },
300
+ { variant: 'text', color: 'danger', class: 'text-error' },
301
+ { variant: 'text', color: 'warning', class: 'text-warning' },
302
+ { variant: 'text', color: 'success', class: 'text-success' },
303
+ { variant: 'text', color: 'accent', class: 'text-accent' },
304
+ { variant: 'text', color: 'gray', class: 'text-base-content/70' },
305
+ // Text — hover
306
+ { variant: 'text', color: 'primary', hover: true, class: 'hover:bg-primary/10' },
307
+ { variant: 'text', color: 'secondary', hover: true, class: 'hover:bg-neutral/10' },
308
+ { variant: 'text', color: 'danger', hover: true, class: 'hover:bg-error/10' },
309
+ { variant: 'text', color: 'warning', hover: true, class: 'hover:bg-warning/10' },
310
+ { variant: 'text', color: 'success', hover: true, class: 'hover:bg-success/10' },
311
+ { variant: 'text', color: 'accent', hover: true, class: 'hover:bg-accent/10' },
312
+ { variant: 'text', color: 'gray', hover: true, class: 'hover:bg-base-content/10' },
313
+ // Text Underline — base
314
+ { variant: 'textUnderline', color: 'primary', class: 'text-primary underline underline-offset-2' },
315
+ { variant: 'textUnderline', color: 'secondary', class: 'text-neutral underline underline-offset-2' },
316
+ { variant: 'textUnderline', color: 'danger', class: 'text-error underline underline-offset-2' },
317
+ { variant: 'textUnderline', color: 'warning', class: 'text-warning underline underline-offset-2' },
318
+ { variant: 'textUnderline', color: 'success', class: 'text-success underline underline-offset-2' },
319
+ { variant: 'textUnderline', color: 'accent', class: 'text-accent underline underline-offset-2' },
320
+ { variant: 'textUnderline', color: 'gray', class: 'text-base-content/70 underline underline-offset-2' },
321
+ // Text Underline — hover
322
+ { variant: 'textUnderline', color: 'primary', hover: true, class: 'hover:bg-primary/10' },
323
+ { variant: 'textUnderline', color: 'secondary', hover: true, class: 'hover:bg-neutral/10' },
324
+ { variant: 'textUnderline', color: 'danger', hover: true, class: 'hover:bg-error/10' },
325
+ { variant: 'textUnderline', color: 'warning', hover: true, class: 'hover:bg-warning/10' },
326
+ { variant: 'textUnderline', color: 'success', hover: true, class: 'hover:bg-success/10' },
327
+ { variant: 'textUnderline', color: 'accent', hover: true, class: 'hover:bg-accent/10' },
328
+ { variant: 'textUnderline', color: 'gray', hover: true, class: 'hover:bg-base-content/10' },
294
329
  ],
295
330
  defaultVariants: {
296
331
  size: 'md',
@@ -299,6 +334,7 @@ const mnButtonVariants = tv({
299
334
  borderRadius: 'lg',
300
335
  disabled: false,
301
336
  wrap: false,
337
+ hover: true,
302
338
  },
303
339
  });
304
340
 
@@ -313,6 +349,7 @@ class MnButton {
313
349
  borderRadius: this.data.borderRadius,
314
350
  disabled: this.data.disabled,
315
351
  wrap: this.data.wrap,
352
+ hover: this.data.hover,
316
353
  });
317
354
  }
318
355
  // For accessibility (works for both <button> and <a>)
@@ -2607,11 +2644,11 @@ class MnMultiSelect {
2607
2644
  });
2608
2645
  }
2609
2646
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnMultiSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
2610
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: MnMultiSelect, isStandalone: true, selector: "mn-lib-multi-select", inputs: { props: "props" }, host: { listeners: { "document:click": "onDocumentClick($event)", "window:scroll": "onWindowScrollOrResize()", "window:resize": "onWindowScrollOrResize()" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true }], ngImport: i0, template: "<div class=\"flex flex-col h-full\" [class.is-fullwidth]=\"props.fullWidth\">\n @if (uiConfig.label || props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ uiConfig.label || props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <!-- Trigger -->\n <div\n #trigger\n [id]=\"resolvedId\"\n [ngClass]=\"triggerClasses\"\n class=\"relative\"\n [attr.aria-label]=\"uiConfig.ariaLabel || uiConfig.label || props.label || null\"\n [attr.aria-invalid]=\"showError || null\"\n [attr.aria-describedby]=\"showError ? resolvedId + '-error' : null\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"isOpen ? resolvedId + '-listbox' : null\"\n role=\"combobox\"\n tabindex=\"0\"\n (click)=\"toggle()\"\n (keydown.enter)=\"toggle()\"\n (keydown.space)=\"toggle(); $event.preventDefault()\"\n (blur)=\"handleBlur()\"\n >\n <div class=\"flex flex-row items-center gap-x-2 flex-wrap min-h-[1.5rem]\">\n @if (selectedOptions.length === 0) {\n <span class=\"text-base-content/50\">{{ uiConfig.placeholder || props.placeholder || 'Select...' }}</span>\n } @else {\n @for (opt of selectedOptions; track opt.value) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <span class=\"inline-flex items-center gap-x-1 bg-base-200 border border-accent text-base-content text-xs pl-2 py-0.5 rounded-md cursor-pointer\"\n (click)=\"removeOption(opt, $event)\">\n {{ opt.label }}\n <button\n mnButton\n [data]=\"{ size: 'sm', variant: 'text', color: 'secondary' }\"\n type=\"button\"\n class=\"text-base-content/50 hover:text-base-content cursor-pointer\"\n (click)=\"removeOption(opt, $event)\"\n [attr.aria-label]=\"'Remove ' + opt.label\"\n ><svg lucideX [size]=\"18\"></svg></button>\n </span>\n }\n }\n </div>\n <div class=\"absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none\">\n <svg class=\"w-4 h-4 text-base-content/50\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 12 12\">\n <path fill=\"currentColor\" d=\"M6 8L1 3h10z\"/>\n </svg>\n </div>\n </div>\n\n <!-- Dropdown -->\n @if (isOpen) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n [id]=\"resolvedId + '-listbox'\"\n aria-multiselectable=\"true\"\n role=\"listbox\"\n class=\"fixed z-[9999] bg-base-100 border border-base-300 rounded-md shadow-lg max-h-60 overflow-auto\"\n [style.top]=\"dropdownStyle.top\"\n [style.left]=\"dropdownStyle.left\"\n [style.width]=\"dropdownStyle.width\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (props.searchable) {\n <div class=\"p-2 border-b border-base-300\">\n <input\n type=\"text\"\n class=\"w-full p-1.5 text-sm border border-base-300 rounded-md outline-none focus:border-brand-500 bg-base-200 text-base-content placeholder-base-content/50\"\n [placeholder]=\"props.searchPlaceholder || 'Search...'\"\n [value]=\"searchTerm\"\n (input)=\"onSearch(($any($event.target)).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n @for (opt of filteredOptions; track opt.value) {\n <div\n (keyup.enter)=\"toggleOption(opt)\"\n (keyup.space)=\"toggleOption(opt)\"\n [attr.aria-selected]=\"isSelected(opt)\"\n class=\"flex items-center gap-x-2 px-3 py-2 text-sm cursor-pointer text-base-content hover:bg-base-200\"\n [class.opacity-50]=\"opt.disabled || isMaxReached(opt)\"\n [class.pointer-events-none]=\"opt.disabled || isMaxReached(opt)\"\n (click)=\"toggleOption(opt); $event.stopPropagation()\"\n role=\"option\"\n tabindex=\"0\"\n >\n <input\n type=\"checkbox\"\n class=\"w-4 h-4 accent-brand-500 pointer-events-none\"\n [checked]=\"isSelected(opt)\"\n [disabled]=\"opt.disabled || isMaxReached(opt)\"\n tabindex=\"-1\"\n />\n <span>{{ opt.label }}</span>\n </div>\n }\n @if (filteredOptions.length === 0) {\n <div class=\"px-3 py-2 text-sm text-base-content/50\">{{ uiConfig.noOptionsFound || 'No options found' }}</div>\n }\n </div>\n }\n\n @if (showError) {\n @if (props.showAllErrors) {\n <div class=\"flex flex-col gap-y-1 mt-1\">\n @for (error of errorMessages; track $index) {\n <mn-error-message [errorMessage]=\"error\" [id]=\"resolvedId + '-' + $index\"></mn-error-message>\n }\n </div>\n } @else {\n @if (errorMessage !== null) {\n <mn-error-message [errorMessage]=\"errorMessage\" [id]=\"resolvedId\"></mn-error-message>\n }\n }\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MnErrorMessage, selector: "mn-error-message", inputs: ["errorMessage", "id"] }, { kind: "component", type: MnButton, selector: "button[mnButton], a[mnButton]", inputs: ["data"] }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }] });
2647
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: MnMultiSelect, isStandalone: true, selector: "mn-lib-multi-select", inputs: { props: "props" }, host: { listeners: { "document:click": "onDocumentClick($event)", "window:scroll": "onWindowScrollOrResize()", "window:resize": "onWindowScrollOrResize()" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true }], ngImport: i0, template: "<div class=\"flex flex-col h-full\" [class.is-fullwidth]=\"props.fullWidth\">\n @if (uiConfig.label || props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ uiConfig.label || props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <!-- Trigger -->\n <div\n #trigger\n [id]=\"resolvedId\"\n [ngClass]=\"triggerClasses\"\n class=\"relative\"\n [attr.aria-label]=\"uiConfig.ariaLabel || uiConfig.label || props.label || null\"\n [attr.aria-invalid]=\"showError || null\"\n [attr.aria-describedby]=\"showError ? resolvedId + '-error' : null\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"isOpen ? resolvedId + '-listbox' : null\"\n role=\"combobox\"\n tabindex=\"0\"\n (click)=\"toggle()\"\n (keydown.enter)=\"toggle()\"\n (keydown.space)=\"toggle(); $event.preventDefault()\"\n (blur)=\"handleBlur()\"\n >\n <div class=\"flex flex-row items-center gap-x-2 flex-wrap min-h-6\">\n @if (selectedOptions.length === 0) {\n <span class=\"text-base-content/50\">{{ uiConfig.placeholder || props.placeholder || 'Select...' }}</span>\n } @else {\n @for (opt of selectedOptions; track opt.value) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <span class=\"inline-flex items-center gap-x-1 bg-base-200 border border-accent text-base-content text-xs pl-2 py-0.5 rounded-md cursor-pointer\"\n (click)=\"removeOption(opt, $event)\">\n {{ opt.label }}\n <button\n mnButton\n [data]=\"{ size: 'sm', variant: 'text', color: 'secondary', hover: false }\"\n type=\"button\"\n class=\"text-base-content/50 cursor-pointer\"\n (click)=\"removeOption(opt, $event)\"\n [attr.aria-label]=\"'Remove ' + opt.label\"\n ><svg lucideX [size]=\"18\"></svg></button>\n </span>\n }\n }\n </div>\n <div class=\"absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none\">\n <svg class=\"w-4 h-4 text-base-content/50\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 12 12\">\n <path fill=\"currentColor\" d=\"M6 8L1 3h10z\"/>\n </svg>\n </div>\n </div>\n\n <!-- Dropdown -->\n @if (isOpen) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n [id]=\"resolvedId + '-listbox'\"\n aria-multiselectable=\"true\"\n role=\"listbox\"\n class=\"fixed z-9999 bg-base-100 border border-base-300 rounded-md shadow-lg max-h-60 overflow-auto\"\n [style.top]=\"dropdownStyle.top\"\n [style.left]=\"dropdownStyle.left\"\n [style.width]=\"dropdownStyle.width\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (props.searchable) {\n <div class=\"p-2 border-b border-base-300\">\n <input\n type=\"text\"\n class=\"w-full p-1.5 text-sm border border-base-300 rounded-md outline-none focus:border-brand-500 bg-base-200 text-base-content placeholder-base-content/50\"\n [placeholder]=\"props.searchPlaceholder || 'Search...'\"\n [value]=\"searchTerm\"\n (input)=\"onSearch(($any($event.target)).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n @for (opt of filteredOptions; track opt.value) {\n <div\n (keyup.enter)=\"toggleOption(opt)\"\n (keyup.space)=\"toggleOption(opt)\"\n [attr.aria-selected]=\"isSelected(opt)\"\n class=\"flex items-center gap-x-2 px-3 py-2 text-sm cursor-pointer text-base-content hover:bg-base-200\"\n [class.opacity-50]=\"opt.disabled || isMaxReached(opt)\"\n [class.pointer-events-none]=\"opt.disabled || isMaxReached(opt)\"\n (click)=\"toggleOption(opt); $event.stopPropagation()\"\n role=\"option\"\n tabindex=\"0\"\n >\n <input\n type=\"checkbox\"\n class=\"w-4 h-4 accent-brand-500 pointer-events-none\"\n [checked]=\"isSelected(opt)\"\n [disabled]=\"opt.disabled || isMaxReached(opt)\"\n tabindex=\"-1\"\n />\n <span>{{ opt.label }}</span>\n </div>\n }\n @if (filteredOptions.length === 0) {\n <div class=\"px-3 py-2 text-sm text-base-content/50\">{{ uiConfig.noOptionsFound || 'No options found' }}</div>\n }\n </div>\n }\n\n @if (showError) {\n @if (props.showAllErrors) {\n <div class=\"flex flex-col gap-y-1 mt-1\">\n @for (error of errorMessages; track $index) {\n <mn-error-message [errorMessage]=\"error\" [id]=\"resolvedId + '-' + $index\"></mn-error-message>\n }\n </div>\n } @else {\n @if (errorMessage !== null) {\n <mn-error-message [errorMessage]=\"errorMessage\" [id]=\"resolvedId\"></mn-error-message>\n }\n }\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MnErrorMessage, selector: "mn-error-message", inputs: ["errorMessage", "id"] }, { kind: "component", type: MnButton, selector: "button[mnButton], a[mnButton]", inputs: ["data"] }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }] });
2611
2648
  }
2612
2649
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnMultiSelect, decorators: [{
2613
2650
  type: Component,
2614
- args: [{ selector: 'mn-lib-multi-select', standalone: true, imports: [NgClass, MnErrorMessage, MnButton, LucideX], template: "<div class=\"flex flex-col h-full\" [class.is-fullwidth]=\"props.fullWidth\">\n @if (uiConfig.label || props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ uiConfig.label || props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <!-- Trigger -->\n <div\n #trigger\n [id]=\"resolvedId\"\n [ngClass]=\"triggerClasses\"\n class=\"relative\"\n [attr.aria-label]=\"uiConfig.ariaLabel || uiConfig.label || props.label || null\"\n [attr.aria-invalid]=\"showError || null\"\n [attr.aria-describedby]=\"showError ? resolvedId + '-error' : null\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"isOpen ? resolvedId + '-listbox' : null\"\n role=\"combobox\"\n tabindex=\"0\"\n (click)=\"toggle()\"\n (keydown.enter)=\"toggle()\"\n (keydown.space)=\"toggle(); $event.preventDefault()\"\n (blur)=\"handleBlur()\"\n >\n <div class=\"flex flex-row items-center gap-x-2 flex-wrap min-h-[1.5rem]\">\n @if (selectedOptions.length === 0) {\n <span class=\"text-base-content/50\">{{ uiConfig.placeholder || props.placeholder || 'Select...' }}</span>\n } @else {\n @for (opt of selectedOptions; track opt.value) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <span class=\"inline-flex items-center gap-x-1 bg-base-200 border border-accent text-base-content text-xs pl-2 py-0.5 rounded-md cursor-pointer\"\n (click)=\"removeOption(opt, $event)\">\n {{ opt.label }}\n <button\n mnButton\n [data]=\"{ size: 'sm', variant: 'text', color: 'secondary' }\"\n type=\"button\"\n class=\"text-base-content/50 hover:text-base-content cursor-pointer\"\n (click)=\"removeOption(opt, $event)\"\n [attr.aria-label]=\"'Remove ' + opt.label\"\n ><svg lucideX [size]=\"18\"></svg></button>\n </span>\n }\n }\n </div>\n <div class=\"absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none\">\n <svg class=\"w-4 h-4 text-base-content/50\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 12 12\">\n <path fill=\"currentColor\" d=\"M6 8L1 3h10z\"/>\n </svg>\n </div>\n </div>\n\n <!-- Dropdown -->\n @if (isOpen) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n [id]=\"resolvedId + '-listbox'\"\n aria-multiselectable=\"true\"\n role=\"listbox\"\n class=\"fixed z-[9999] bg-base-100 border border-base-300 rounded-md shadow-lg max-h-60 overflow-auto\"\n [style.top]=\"dropdownStyle.top\"\n [style.left]=\"dropdownStyle.left\"\n [style.width]=\"dropdownStyle.width\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (props.searchable) {\n <div class=\"p-2 border-b border-base-300\">\n <input\n type=\"text\"\n class=\"w-full p-1.5 text-sm border border-base-300 rounded-md outline-none focus:border-brand-500 bg-base-200 text-base-content placeholder-base-content/50\"\n [placeholder]=\"props.searchPlaceholder || 'Search...'\"\n [value]=\"searchTerm\"\n (input)=\"onSearch(($any($event.target)).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n @for (opt of filteredOptions; track opt.value) {\n <div\n (keyup.enter)=\"toggleOption(opt)\"\n (keyup.space)=\"toggleOption(opt)\"\n [attr.aria-selected]=\"isSelected(opt)\"\n class=\"flex items-center gap-x-2 px-3 py-2 text-sm cursor-pointer text-base-content hover:bg-base-200\"\n [class.opacity-50]=\"opt.disabled || isMaxReached(opt)\"\n [class.pointer-events-none]=\"opt.disabled || isMaxReached(opt)\"\n (click)=\"toggleOption(opt); $event.stopPropagation()\"\n role=\"option\"\n tabindex=\"0\"\n >\n <input\n type=\"checkbox\"\n class=\"w-4 h-4 accent-brand-500 pointer-events-none\"\n [checked]=\"isSelected(opt)\"\n [disabled]=\"opt.disabled || isMaxReached(opt)\"\n tabindex=\"-1\"\n />\n <span>{{ opt.label }}</span>\n </div>\n }\n @if (filteredOptions.length === 0) {\n <div class=\"px-3 py-2 text-sm text-base-content/50\">{{ uiConfig.noOptionsFound || 'No options found' }}</div>\n }\n </div>\n }\n\n @if (showError) {\n @if (props.showAllErrors) {\n <div class=\"flex flex-col gap-y-1 mt-1\">\n @for (error of errorMessages; track $index) {\n <mn-error-message [errorMessage]=\"error\" [id]=\"resolvedId + '-' + $index\"></mn-error-message>\n }\n </div>\n } @else {\n @if (errorMessage !== null) {\n <mn-error-message [errorMessage]=\"errorMessage\" [id]=\"resolvedId\"></mn-error-message>\n }\n }\n }\n</div>\n" }]
2651
+ args: [{ selector: 'mn-lib-multi-select', standalone: true, imports: [NgClass, MnErrorMessage, MnButton, LucideX], template: "<div class=\"flex flex-col h-full\" [class.is-fullwidth]=\"props.fullWidth\">\n @if (uiConfig.label || props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ uiConfig.label || props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <!-- Trigger -->\n <div\n #trigger\n [id]=\"resolvedId\"\n [ngClass]=\"triggerClasses\"\n class=\"relative\"\n [attr.aria-label]=\"uiConfig.ariaLabel || uiConfig.label || props.label || null\"\n [attr.aria-invalid]=\"showError || null\"\n [attr.aria-describedby]=\"showError ? resolvedId + '-error' : null\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-controls]=\"isOpen ? resolvedId + '-listbox' : null\"\n role=\"combobox\"\n tabindex=\"0\"\n (click)=\"toggle()\"\n (keydown.enter)=\"toggle()\"\n (keydown.space)=\"toggle(); $event.preventDefault()\"\n (blur)=\"handleBlur()\"\n >\n <div class=\"flex flex-row items-center gap-x-2 flex-wrap min-h-6\">\n @if (selectedOptions.length === 0) {\n <span class=\"text-base-content/50\">{{ uiConfig.placeholder || props.placeholder || 'Select...' }}</span>\n } @else {\n @for (opt of selectedOptions; track opt.value) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <span class=\"inline-flex items-center gap-x-1 bg-base-200 border border-accent text-base-content text-xs pl-2 py-0.5 rounded-md cursor-pointer\"\n (click)=\"removeOption(opt, $event)\">\n {{ opt.label }}\n <button\n mnButton\n [data]=\"{ size: 'sm', variant: 'text', color: 'secondary', hover: false }\"\n type=\"button\"\n class=\"text-base-content/50 cursor-pointer\"\n (click)=\"removeOption(opt, $event)\"\n [attr.aria-label]=\"'Remove ' + opt.label\"\n ><svg lucideX [size]=\"18\"></svg></button>\n </span>\n }\n }\n </div>\n <div class=\"absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none\">\n <svg class=\"w-4 h-4 text-base-content/50\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 12 12\">\n <path fill=\"currentColor\" d=\"M6 8L1 3h10z\"/>\n </svg>\n </div>\n </div>\n\n <!-- Dropdown -->\n @if (isOpen) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n [id]=\"resolvedId + '-listbox'\"\n aria-multiselectable=\"true\"\n role=\"listbox\"\n class=\"fixed z-9999 bg-base-100 border border-base-300 rounded-md shadow-lg max-h-60 overflow-auto\"\n [style.top]=\"dropdownStyle.top\"\n [style.left]=\"dropdownStyle.left\"\n [style.width]=\"dropdownStyle.width\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (props.searchable) {\n <div class=\"p-2 border-b border-base-300\">\n <input\n type=\"text\"\n class=\"w-full p-1.5 text-sm border border-base-300 rounded-md outline-none focus:border-brand-500 bg-base-200 text-base-content placeholder-base-content/50\"\n [placeholder]=\"props.searchPlaceholder || 'Search...'\"\n [value]=\"searchTerm\"\n (input)=\"onSearch(($any($event.target)).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n @for (opt of filteredOptions; track opt.value) {\n <div\n (keyup.enter)=\"toggleOption(opt)\"\n (keyup.space)=\"toggleOption(opt)\"\n [attr.aria-selected]=\"isSelected(opt)\"\n class=\"flex items-center gap-x-2 px-3 py-2 text-sm cursor-pointer text-base-content hover:bg-base-200\"\n [class.opacity-50]=\"opt.disabled || isMaxReached(opt)\"\n [class.pointer-events-none]=\"opt.disabled || isMaxReached(opt)\"\n (click)=\"toggleOption(opt); $event.stopPropagation()\"\n role=\"option\"\n tabindex=\"0\"\n >\n <input\n type=\"checkbox\"\n class=\"w-4 h-4 accent-brand-500 pointer-events-none\"\n [checked]=\"isSelected(opt)\"\n [disabled]=\"opt.disabled || isMaxReached(opt)\"\n tabindex=\"-1\"\n />\n <span>{{ opt.label }}</span>\n </div>\n }\n @if (filteredOptions.length === 0) {\n <div class=\"px-3 py-2 text-sm text-base-content/50\">{{ uiConfig.noOptionsFound || 'No options found' }}</div>\n }\n </div>\n }\n\n @if (showError) {\n @if (props.showAllErrors) {\n <div class=\"flex flex-col gap-y-1 mt-1\">\n @for (error of errorMessages; track $index) {\n <mn-error-message [errorMessage]=\"error\" [id]=\"resolvedId + '-' + $index\"></mn-error-message>\n }\n </div>\n } @else {\n @if (errorMessage !== null) {\n <mn-error-message [errorMessage]=\"errorMessage\" [id]=\"resolvedId\"></mn-error-message>\n }\n }\n }\n</div>\n" }]
2615
2652
  }], ctorParameters: () => [], propDecorators: { props: [{
2616
2653
  type: Input,
2617
2654
  args: [{ required: true }]