@lifecorner/angular-ui-kit 3.15.0 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +2 -1
  2. package/fesm2022/lifecorner-angular-ui-kit-icon.mjs +2 -2
  3. package/fesm2022/lifecorner-angular-ui-kit-icon.mjs.map +1 -1
  4. package/fesm2022/lifecorner-angular-ui-kit-icons.mjs +26 -2
  5. package/fesm2022/lifecorner-angular-ui-kit-icons.mjs.map +1 -1
  6. package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs +10 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs.map +1 -1
  8. package/fesm2022/lifecorner-angular-ui-kit-list.mjs +29 -6
  9. package/fesm2022/lifecorner-angular-ui-kit-list.mjs.map +1 -1
  10. package/fesm2022/lifecorner-angular-ui-kit-money.mjs +250 -0
  11. package/fesm2022/lifecorner-angular-ui-kit-money.mjs.map +1 -0
  12. package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs +21 -2
  13. package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs.map +1 -1
  14. package/fesm2022/lifecorner-angular-ui-kit-switch.mjs +21 -4
  15. package/fesm2022/lifecorner-angular-ui-kit-switch.mjs.map +1 -1
  16. package/fesm2022/lifecorner-angular-ui-kit-table.mjs +71 -7
  17. package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
  18. package/fesm2022/lifecorner-angular-ui-kit.mjs +1 -0
  19. package/package.json +5 -1
  20. package/src/styles/theme-contrast.spec.ts +22 -0
  21. package/types/lifecorner-angular-ui-kit-icon.d.ts +2 -1
  22. package/types/lifecorner-angular-ui-kit-icons.d.ts +5 -1
  23. package/types/lifecorner-angular-ui-kit-list.d.ts +38 -0
  24. package/types/lifecorner-angular-ui-kit-money.d.ts +150 -0
  25. package/types/lifecorner-angular-ui-kit-next-step-card.d.ts +26 -0
  26. package/types/lifecorner-angular-ui-kit-switch.d.ts +19 -0
  27. package/types/lifecorner-angular-ui-kit-table.d.ts +42 -1
  28. package/types/lifecorner-angular-ui-kit.d.ts +1 -0
@@ -12,8 +12,10 @@ var SwitchComponent = class SwitchComponent {
12
12
  label = input("", ...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
13
13
  labelPosition = input("right", ...ngDevMode ? [{ debugName: "labelPosition" }] : /* istanbul ignore next */ []);
14
14
  ariaLabel = input(void 0, ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
15
+ helpText = input("", ...ngDevMode ? [{ debugName: "helpText" }] : /* istanbul ignore next */ []);
15
16
  checkedChange = output();
16
17
  switchId = `lc-switch-${nextUniqueId++}`;
18
+ helpTextId = computed(() => this.helpText() ? `${this.switchId}-help` : null, ...ngDevMode ? [{ debugName: "helpTextId" }] : /* istanbul ignore next */ []);
17
19
  checkedState = linkedSignal(() => this.checked(), ...ngDevMode ? [{ debugName: "checkedState" }] : /* istanbul ignore next */ []);
18
20
  formDisabled = signal(false, ...ngDevMode ? [{ debugName: "formDisabled" }] : /* istanbul ignore next */ []);
19
21
  isDisabled = computed(() => this.disabled() || this.formDisabled(), ...ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []);
@@ -130,6 +132,13 @@ var SwitchComponent = class SwitchComponent {
130
132
  isSignal: true,
131
133
  isRequired: false,
132
134
  transformFunction: null
135
+ },
136
+ helpText: {
137
+ classPropertyName: "helpText",
138
+ publicName: "helpText",
139
+ isSignal: true,
140
+ isRequired: false,
141
+ transformFunction: null
133
142
  }
134
143
  },
135
144
  outputs: { checkedChange: "checkedChange" },
@@ -139,8 +148,8 @@ var SwitchComponent = class SwitchComponent {
139
148
  multi: true
140
149
  }],
141
150
  ngImport: i0,
142
- template: "<div\n class=\"lc-switch-wrapper\"\n [class.lc-switch-wrapper--label-left]=\"labelPosition() === 'left'\"\n [class.lc-switch-wrapper--label-right]=\"labelPosition() === 'right'\"\n>\n @if (label() && labelPosition() === 'left') {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n\n <!-- Native button: labelable (label for/id works), focusable and Space/Enter toggle it without custom key handling -->\n <button\n type=\"button\"\n [id]=\"switchId\"\n [class]=\"switchClasses()\"\n role=\"switch\"\n [attr.aria-checked]=\"checkedState()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [attr.aria-disabled]=\"loading() && !isDisabled() ? true : null\"\n [disabled]=\"isDisabled()\"\n (click)=\"onClick()\"\n >\n <span class=\"lc-switch-track\">\n <span class=\"lc-switch-thumb\">\n @if (loading()) {\n <span class=\"lc-switch-loading-spinner\"></span>\n }\n </span>\n </span>\n </button>\n\n @if (label() && labelPosition() === 'right') {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n</div>\n",
143
- styles: [".lc-switch-wrapper{display:flex;align-items:center;gap:var(--spacing-3)}.lc-switch-wrapper.lc-switch-wrapper--label-left{flex-direction:row}.lc-switch-wrapper.lc-switch-wrapper--label-right{flex-direction:row-reverse;justify-content:flex-end}.lc-switch-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-switch{position:relative;display:inline-flex;padding:0;border:0;background:transparent;font:inherit;appearance:none;cursor:pointer;-webkit-user-select:none;user-select:none}.lc-switch:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px;border-radius:var(--border-radius-full)}.lc-switch.lc-switch--xs .lc-switch-track{width:2rem;height:1rem}.lc-switch.lc-switch--xs .lc-switch-thumb{width:.75rem;height:.75rem}.lc-switch.lc-switch--xs.lc-switch--checked .lc-switch-thumb{transform:translate(1rem)}.lc-switch.lc-switch--sm .lc-switch-track{width:2.5rem;height:1.25rem}.lc-switch.lc-switch--sm .lc-switch-thumb{width:1rem;height:1rem}.lc-switch.lc-switch--sm.lc-switch--checked .lc-switch-thumb{transform:translate(1.25rem)}.lc-switch.lc-switch--md .lc-switch-track{width:3rem;height:1.5rem}.lc-switch.lc-switch--md .lc-switch-thumb{width:1.25rem;height:1.25rem}.lc-switch.lc-switch--md.lc-switch--checked .lc-switch-thumb{transform:translate(1.5rem)}.lc-switch.lc-switch--lg .lc-switch-track{width:3.5rem;height:1.75rem}.lc-switch.lc-switch--lg .lc-switch-thumb{width:1.5rem;height:1.5rem}.lc-switch.lc-switch--lg.lc-switch--checked .lc-switch-thumb{transform:translate(1.75rem)}.lc-switch.lc-switch--primary.lc-switch--checked .lc-switch-track{background-color:var(--color-primary-fill)}.lc-switch.lc-switch--secondary.lc-switch--checked .lc-switch-track{background-color:var(--color-secondary-fill)}.lc-switch.lc-switch--success.lc-switch--checked .lc-switch-track{background-color:var(--color-success-default)}.lc-switch.lc-switch--warning.lc-switch--checked .lc-switch-track{background-color:var(--color-warning-default)}.lc-switch.lc-switch--danger.lc-switch--checked .lc-switch-track{background-color:var(--color-error-default)}.lc-switch.lc-switch--disabled{cursor:not-allowed;opacity:.5}.lc-switch.lc-switch--loading{cursor:wait}.lc-switch.lc-switch--error .lc-switch-track{background-color:var(--color-error-600)}.lc-switch-track{position:relative;display:flex;align-items:center;background-color:var(--color-neutral-300);border-radius:var(--border-radius-full);transition:background-color var(--animation-duration-base) var(--animation-timing-ease);padding:2px}.lc-switch-thumb{position:relative;display:flex;align-items:center;justify-content:center;background-color:var(--color-neutral-white);border-radius:var(--border-radius-full);box-shadow:var(--elevation-1);transition:transform var(--animation-duration-base) var(--animation-timing-ease);transform:translate(0)}.lc-switch-loading-spinner{width:60%;height:60%;border:2px solid var(--color-neutral-300);border-top-color:var(--color-primary-fill);border-radius:var(--border-radius-full);animation:spin .8s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"],
151
+ template: "<div\n class=\"lc-switch-wrapper\"\n [class.lc-switch-wrapper--label-left]=\"labelPosition() === 'left'\"\n [class.lc-switch-wrapper--label-right]=\"labelPosition() === 'right'\"\n [class.lc-switch-wrapper--help]=\"!!helpTextId()\"\n>\n @if (labelPosition() === 'left') {\n @if (helpTextId(); as helpId) {\n <!-- Label and help text in one column; the label's ::after covers it, so a tap on the help toggles too. -->\n <div class=\"lc-switch-text lc-switch-text--{{ size() }}\">\n @if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n <div [id]=\"helpId\" class=\"lc-switch-help\">{{ helpText() }}</div>\n </div>\n } @else if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n }\n\n <!-- Native button: labelable (label for/id works), focusable and Space/Enter toggle it without custom key handling -->\n <button\n type=\"button\"\n [id]=\"switchId\"\n [class]=\"switchClasses()\"\n role=\"switch\"\n [attr.aria-checked]=\"checkedState()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"helpTextId()\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [attr.aria-disabled]=\"loading() && !isDisabled() ? true : null\"\n [disabled]=\"isDisabled()\"\n (click)=\"onClick()\"\n >\n <span class=\"lc-switch-track\">\n <span class=\"lc-switch-thumb\">\n @if (loading()) {\n <span class=\"lc-switch-loading-spinner\"></span>\n }\n </span>\n </span>\n </button>\n\n @if (labelPosition() === 'right') {\n @if (helpTextId(); as helpId) {\n <div class=\"lc-switch-text lc-switch-text--{{ size() }}\">\n @if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n <div [id]=\"helpId\" class=\"lc-switch-help\">{{ helpText() }}</div>\n </div>\n } @else if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n }\n</div>\n",
152
+ styles: [".lc-switch-wrapper{display:flex;align-items:center;gap:var(--spacing-3)}.lc-switch-wrapper.lc-switch-wrapper--label-left{flex-direction:row}.lc-switch-wrapper.lc-switch-wrapper--label-right{flex-direction:row-reverse;justify-content:flex-end}.lc-switch-wrapper>.lc-switch-label{display:flex;align-items:center;min-height:var(--lc-switch-min-height, var(--size-min-touch-height, 2.75rem))}.lc-switch-wrapper.lc-switch-wrapper--help{align-items:flex-start;min-height:var(--lc-switch-min-height, var(--size-min-touch-height, 2.75rem))}.lc-switch-wrapper.lc-switch-wrapper--help>.lc-switch--sm{margin-top:.03125rem}.lc-switch-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-switch-text{position:relative;display:flex;flex-direction:column;gap:var(--spacing-1);min-width:0;overflow-wrap:anywhere}.lc-switch-text.lc-switch-text--md{padding-top:.09375rem}.lc-switch-text.lc-switch-text--lg{padding-top:.21875rem}.lc-switch-text>.lc-switch-label:after{content:\"\";position:absolute;inset:0}.lc-switch-help{font-size:var(--font-size-sm);font-weight:var(--font-weight-normal);line-height:1.25rem;color:var(--color-text-secondary)}.lc-switch{position:relative;display:inline-flex;padding:0;border:0;background:transparent;font:inherit;appearance:none;cursor:pointer;-webkit-user-select:none;user-select:none}.lc-switch:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px;border-radius:var(--border-radius-full)}.lc-switch.lc-switch--xs .lc-switch-track{width:2rem;height:1rem}.lc-switch.lc-switch--xs .lc-switch-thumb{width:.75rem;height:.75rem}.lc-switch.lc-switch--xs.lc-switch--checked .lc-switch-thumb{transform:translate(1rem)}.lc-switch.lc-switch--sm .lc-switch-track{width:2.5rem;height:1.25rem}.lc-switch.lc-switch--sm .lc-switch-thumb{width:1rem;height:1rem}.lc-switch.lc-switch--sm.lc-switch--checked .lc-switch-thumb{transform:translate(1.25rem)}.lc-switch.lc-switch--md .lc-switch-track{width:3rem;height:1.5rem}.lc-switch.lc-switch--md .lc-switch-thumb{width:1.25rem;height:1.25rem}.lc-switch.lc-switch--md.lc-switch--checked .lc-switch-thumb{transform:translate(1.5rem)}.lc-switch.lc-switch--lg .lc-switch-track{width:3.5rem;height:1.75rem}.lc-switch.lc-switch--lg .lc-switch-thumb{width:1.5rem;height:1.5rem}.lc-switch.lc-switch--lg.lc-switch--checked .lc-switch-thumb{transform:translate(1.75rem)}.lc-switch.lc-switch--primary.lc-switch--checked .lc-switch-track{background-color:var(--color-primary-fill)}.lc-switch.lc-switch--secondary.lc-switch--checked .lc-switch-track{background-color:var(--color-secondary-fill)}.lc-switch.lc-switch--success.lc-switch--checked .lc-switch-track{background-color:var(--color-success-default)}.lc-switch.lc-switch--warning.lc-switch--checked .lc-switch-track{background-color:var(--color-warning-default)}.lc-switch.lc-switch--danger.lc-switch--checked .lc-switch-track{background-color:var(--color-error-default)}.lc-switch.lc-switch--disabled{cursor:not-allowed;opacity:.5}.lc-switch.lc-switch--loading{cursor:wait}.lc-switch.lc-switch--error .lc-switch-track{background-color:var(--color-error-600)}.lc-switch-track{position:relative;display:flex;align-items:center;background-color:var(--color-neutral-300);border-radius:var(--border-radius-full);transition:background-color var(--animation-duration-base) var(--animation-timing-ease);padding:2px}.lc-switch-thumb{position:relative;display:flex;align-items:center;justify-content:center;background-color:var(--color-neutral-white);border-radius:var(--border-radius-full);box-shadow:var(--elevation-1);transition:transform var(--animation-duration-base) var(--animation-timing-ease);transform:translate(0)}.lc-switch-loading-spinner{width:60%;height:60%;border:2px solid var(--color-neutral-300);border-top-color:var(--color-primary-fill);border-radius:var(--border-radius-full);animation:spin .8s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"],
144
153
  changeDetection: i0.ChangeDetectionStrategy.OnPush
145
154
  });
146
155
  };
@@ -161,8 +170,8 @@ i0.ɵɵngDeclareClassMetadata({
161
170
  useExisting: forwardRef(() => SwitchComponent),
162
171
  multi: true
163
172
  }],
164
- template: "<div\n class=\"lc-switch-wrapper\"\n [class.lc-switch-wrapper--label-left]=\"labelPosition() === 'left'\"\n [class.lc-switch-wrapper--label-right]=\"labelPosition() === 'right'\"\n>\n @if (label() && labelPosition() === 'left') {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n\n <!-- Native button: labelable (label for/id works), focusable and Space/Enter toggle it without custom key handling -->\n <button\n type=\"button\"\n [id]=\"switchId\"\n [class]=\"switchClasses()\"\n role=\"switch\"\n [attr.aria-checked]=\"checkedState()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [attr.aria-disabled]=\"loading() && !isDisabled() ? true : null\"\n [disabled]=\"isDisabled()\"\n (click)=\"onClick()\"\n >\n <span class=\"lc-switch-track\">\n <span class=\"lc-switch-thumb\">\n @if (loading()) {\n <span class=\"lc-switch-loading-spinner\"></span>\n }\n </span>\n </span>\n </button>\n\n @if (label() && labelPosition() === 'right') {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n</div>\n",
165
- styles: [".lc-switch-wrapper{display:flex;align-items:center;gap:var(--spacing-3)}.lc-switch-wrapper.lc-switch-wrapper--label-left{flex-direction:row}.lc-switch-wrapper.lc-switch-wrapper--label-right{flex-direction:row-reverse;justify-content:flex-end}.lc-switch-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-switch{position:relative;display:inline-flex;padding:0;border:0;background:transparent;font:inherit;appearance:none;cursor:pointer;-webkit-user-select:none;user-select:none}.lc-switch:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px;border-radius:var(--border-radius-full)}.lc-switch.lc-switch--xs .lc-switch-track{width:2rem;height:1rem}.lc-switch.lc-switch--xs .lc-switch-thumb{width:.75rem;height:.75rem}.lc-switch.lc-switch--xs.lc-switch--checked .lc-switch-thumb{transform:translate(1rem)}.lc-switch.lc-switch--sm .lc-switch-track{width:2.5rem;height:1.25rem}.lc-switch.lc-switch--sm .lc-switch-thumb{width:1rem;height:1rem}.lc-switch.lc-switch--sm.lc-switch--checked .lc-switch-thumb{transform:translate(1.25rem)}.lc-switch.lc-switch--md .lc-switch-track{width:3rem;height:1.5rem}.lc-switch.lc-switch--md .lc-switch-thumb{width:1.25rem;height:1.25rem}.lc-switch.lc-switch--md.lc-switch--checked .lc-switch-thumb{transform:translate(1.5rem)}.lc-switch.lc-switch--lg .lc-switch-track{width:3.5rem;height:1.75rem}.lc-switch.lc-switch--lg .lc-switch-thumb{width:1.5rem;height:1.5rem}.lc-switch.lc-switch--lg.lc-switch--checked .lc-switch-thumb{transform:translate(1.75rem)}.lc-switch.lc-switch--primary.lc-switch--checked .lc-switch-track{background-color:var(--color-primary-fill)}.lc-switch.lc-switch--secondary.lc-switch--checked .lc-switch-track{background-color:var(--color-secondary-fill)}.lc-switch.lc-switch--success.lc-switch--checked .lc-switch-track{background-color:var(--color-success-default)}.lc-switch.lc-switch--warning.lc-switch--checked .lc-switch-track{background-color:var(--color-warning-default)}.lc-switch.lc-switch--danger.lc-switch--checked .lc-switch-track{background-color:var(--color-error-default)}.lc-switch.lc-switch--disabled{cursor:not-allowed;opacity:.5}.lc-switch.lc-switch--loading{cursor:wait}.lc-switch.lc-switch--error .lc-switch-track{background-color:var(--color-error-600)}.lc-switch-track{position:relative;display:flex;align-items:center;background-color:var(--color-neutral-300);border-radius:var(--border-radius-full);transition:background-color var(--animation-duration-base) var(--animation-timing-ease);padding:2px}.lc-switch-thumb{position:relative;display:flex;align-items:center;justify-content:center;background-color:var(--color-neutral-white);border-radius:var(--border-radius-full);box-shadow:var(--elevation-1);transition:transform var(--animation-duration-base) var(--animation-timing-ease);transform:translate(0)}.lc-switch-loading-spinner{width:60%;height:60%;border:2px solid var(--color-neutral-300);border-top-color:var(--color-primary-fill);border-radius:var(--border-radius-full);animation:spin .8s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"]
173
+ template: "<div\n class=\"lc-switch-wrapper\"\n [class.lc-switch-wrapper--label-left]=\"labelPosition() === 'left'\"\n [class.lc-switch-wrapper--label-right]=\"labelPosition() === 'right'\"\n [class.lc-switch-wrapper--help]=\"!!helpTextId()\"\n>\n @if (labelPosition() === 'left') {\n @if (helpTextId(); as helpId) {\n <!-- Label and help text in one column; the label's ::after covers it, so a tap on the help toggles too. -->\n <div class=\"lc-switch-text lc-switch-text--{{ size() }}\">\n @if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n <div [id]=\"helpId\" class=\"lc-switch-help\">{{ helpText() }}</div>\n </div>\n } @else if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n }\n\n <!-- Native button: labelable (label for/id works), focusable and Space/Enter toggle it without custom key handling -->\n <button\n type=\"button\"\n [id]=\"switchId\"\n [class]=\"switchClasses()\"\n role=\"switch\"\n [attr.aria-checked]=\"checkedState()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"helpTextId()\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [attr.aria-disabled]=\"loading() && !isDisabled() ? true : null\"\n [disabled]=\"isDisabled()\"\n (click)=\"onClick()\"\n >\n <span class=\"lc-switch-track\">\n <span class=\"lc-switch-thumb\">\n @if (loading()) {\n <span class=\"lc-switch-loading-spinner\"></span>\n }\n </span>\n </span>\n </button>\n\n @if (labelPosition() === 'right') {\n @if (helpTextId(); as helpId) {\n <div class=\"lc-switch-text lc-switch-text--{{ size() }}\">\n @if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n <div [id]=\"helpId\" class=\"lc-switch-help\">{{ helpText() }}</div>\n </div>\n } @else if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n }\n</div>\n",
174
+ styles: [".lc-switch-wrapper{display:flex;align-items:center;gap:var(--spacing-3)}.lc-switch-wrapper.lc-switch-wrapper--label-left{flex-direction:row}.lc-switch-wrapper.lc-switch-wrapper--label-right{flex-direction:row-reverse;justify-content:flex-end}.lc-switch-wrapper>.lc-switch-label{display:flex;align-items:center;min-height:var(--lc-switch-min-height, var(--size-min-touch-height, 2.75rem))}.lc-switch-wrapper.lc-switch-wrapper--help{align-items:flex-start;min-height:var(--lc-switch-min-height, var(--size-min-touch-height, 2.75rem))}.lc-switch-wrapper.lc-switch-wrapper--help>.lc-switch--sm{margin-top:.03125rem}.lc-switch-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-switch-text{position:relative;display:flex;flex-direction:column;gap:var(--spacing-1);min-width:0;overflow-wrap:anywhere}.lc-switch-text.lc-switch-text--md{padding-top:.09375rem}.lc-switch-text.lc-switch-text--lg{padding-top:.21875rem}.lc-switch-text>.lc-switch-label:after{content:\"\";position:absolute;inset:0}.lc-switch-help{font-size:var(--font-size-sm);font-weight:var(--font-weight-normal);line-height:1.25rem;color:var(--color-text-secondary)}.lc-switch{position:relative;display:inline-flex;padding:0;border:0;background:transparent;font:inherit;appearance:none;cursor:pointer;-webkit-user-select:none;user-select:none}.lc-switch:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px;border-radius:var(--border-radius-full)}.lc-switch.lc-switch--xs .lc-switch-track{width:2rem;height:1rem}.lc-switch.lc-switch--xs .lc-switch-thumb{width:.75rem;height:.75rem}.lc-switch.lc-switch--xs.lc-switch--checked .lc-switch-thumb{transform:translate(1rem)}.lc-switch.lc-switch--sm .lc-switch-track{width:2.5rem;height:1.25rem}.lc-switch.lc-switch--sm .lc-switch-thumb{width:1rem;height:1rem}.lc-switch.lc-switch--sm.lc-switch--checked .lc-switch-thumb{transform:translate(1.25rem)}.lc-switch.lc-switch--md .lc-switch-track{width:3rem;height:1.5rem}.lc-switch.lc-switch--md .lc-switch-thumb{width:1.25rem;height:1.25rem}.lc-switch.lc-switch--md.lc-switch--checked .lc-switch-thumb{transform:translate(1.5rem)}.lc-switch.lc-switch--lg .lc-switch-track{width:3.5rem;height:1.75rem}.lc-switch.lc-switch--lg .lc-switch-thumb{width:1.5rem;height:1.5rem}.lc-switch.lc-switch--lg.lc-switch--checked .lc-switch-thumb{transform:translate(1.75rem)}.lc-switch.lc-switch--primary.lc-switch--checked .lc-switch-track{background-color:var(--color-primary-fill)}.lc-switch.lc-switch--secondary.lc-switch--checked .lc-switch-track{background-color:var(--color-secondary-fill)}.lc-switch.lc-switch--success.lc-switch--checked .lc-switch-track{background-color:var(--color-success-default)}.lc-switch.lc-switch--warning.lc-switch--checked .lc-switch-track{background-color:var(--color-warning-default)}.lc-switch.lc-switch--danger.lc-switch--checked .lc-switch-track{background-color:var(--color-error-default)}.lc-switch.lc-switch--disabled{cursor:not-allowed;opacity:.5}.lc-switch.lc-switch--loading{cursor:wait}.lc-switch.lc-switch--error .lc-switch-track{background-color:var(--color-error-600)}.lc-switch-track{position:relative;display:flex;align-items:center;background-color:var(--color-neutral-300);border-radius:var(--border-radius-full);transition:background-color var(--animation-duration-base) var(--animation-timing-ease);padding:2px}.lc-switch-thumb{position:relative;display:flex;align-items:center;justify-content:center;background-color:var(--color-neutral-white);border-radius:var(--border-radius-full);box-shadow:var(--elevation-1);transition:transform var(--animation-duration-base) var(--animation-timing-ease);transform:translate(0)}.lc-switch-loading-spinner{width:60%;height:60%;border:2px solid var(--color-neutral-300);border-top-color:var(--color-primary-fill);border-radius:var(--border-radius-full);animation:spin .8s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"]
166
175
  }]
167
176
  }],
168
177
  propDecorators: {
@@ -238,6 +247,14 @@ i0.ɵɵngDeclareClassMetadata({
238
247
  required: false
239
248
  }]
240
249
  }],
250
+ helpText: [{
251
+ type: i0.Input,
252
+ args: [{
253
+ isSignal: true,
254
+ alias: "helpText",
255
+ required: false
256
+ }]
257
+ }],
241
258
  checkedChange: [{
242
259
  type: i0.Output,
243
260
  args: ["checkedChange"]
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-switch.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/switch/switch.component.ts","../../../../libs/angular-ui-kit/src/lib/switch/switch.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n computed,\n forwardRef,\n input,\n linkedSignal,\n output,\n signal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nlet nextUniqueId = 0;\n\n/**\n * Switch component for boolean toggle functionality.\n *\n * Features:\n * - Color variants (primary, secondary, success, warning, danger)\n * - Size options (sm, md, lg)\n * - Loading state with spinner\n * - Configurable label positioning (left/right)\n * - Keyboard toggle support\n * - Disabled and required states\n * - ControlValueAccessor integration for reactive forms\n *\n * @example\n * ```html\n * <lc-switch label=\"Enable notifications\" [(ngModel)]=\"isEnabled\" />\n * ```\n */\n@Component({\n selector: 'lc-switch',\n standalone: true,\n imports: [],\n templateUrl: './switch.component.html',\n styleUrl: './switch.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SwitchComponent),\n multi: true,\n },\n ],\n})\nexport class SwitchComponent implements ControlValueAccessor {\n /**\n * Visual variant of the switch\n */\n readonly variant = input<'primary' | 'secondary' | 'success' | 'warning' | 'danger'>('primary');\n\n /**\n * Size of the switch\n */\n readonly size = input<'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the switch is checked (template-driven usage; forms write via `writeValue`)\n */\n readonly checked = input<boolean>(false);\n\n /**\n * Whether the switch is disabled\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the switch is required\n */\n readonly required = input<boolean>(false);\n\n /**\n * Whether the switch is in loading state\n */\n readonly loading = input<boolean>(false);\n\n /**\n * Label text\n */\n readonly label = input<string>('');\n\n /**\n * Label position relative to switch\n */\n readonly labelPosition = input<'left' | 'right'>('right');\n\n /**\n * ARIA label for accessibility (overrides the visible label as accessible name)\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Emitted when checked state changes\n */\n readonly checkedChange = output<boolean>();\n\n /** Per-instance id so the visible `<label for>` points at this switch. */\n readonly switchId = `lc-switch-${nextUniqueId++}`;\n\n /**\n * Effective checked state: follows the `checked` input, overridden by\n * form writes (`writeValue`) and user toggles.\n */\n readonly checkedState = linkedSignal(() => this.checked());\n\n // Internal disabled state (for ControlValueAccessor)\n private readonly formDisabled = signal<boolean>(false);\n\n /**\n * Computed disabled state from both input and form control\n */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /**\n * Computed classes for the switch element\n */\n readonly switchClasses = computed(() => {\n const classes = ['lc-switch', `lc-switch--${this.variant()}`, `lc-switch--${this.size()}`];\n\n if (this.checkedState()) {\n classes.push('lc-switch--checked');\n }\n\n if (this.isDisabled()) {\n classes.push('lc-switch--disabled');\n }\n\n if (this.loading()) {\n classes.push('lc-switch--loading');\n }\n\n return classes.join(' ');\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: boolean) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n /**\n * Toggle the switch state\n */\n toggle(): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n\n const next = !this.checkedState();\n this.checkedState.set(next);\n this.onChange(next);\n this.onTouched();\n this.checkedChange.emit(next);\n }\n\n /**\n * Handle click event (the control is a native button, so Space/Enter arrive here too)\n */\n onClick(): void {\n this.toggle();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: boolean | null | undefined): void {\n this.checkedState.set(!!value);\n }\n\n registerOnChange(fn: (value: boolean) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n","<div\n class=\"lc-switch-wrapper\"\n [class.lc-switch-wrapper--label-left]=\"labelPosition() === 'left'\"\n [class.lc-switch-wrapper--label-right]=\"labelPosition() === 'right'\"\n>\n @if (label() && labelPosition() === 'left') {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n\n <!-- Native button: labelable (label for/id works), focusable and Space/Enter toggle it without custom key handling -->\n <button\n type=\"button\"\n [id]=\"switchId\"\n [class]=\"switchClasses()\"\n role=\"switch\"\n [attr.aria-checked]=\"checkedState()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [attr.aria-disabled]=\"loading() && !isDisabled() ? true : null\"\n [disabled]=\"isDisabled()\"\n (click)=\"onClick()\"\n >\n <span class=\"lc-switch-track\">\n <span class=\"lc-switch-thumb\">\n @if (loading()) {\n <span class=\"lc-switch-loading-spinner\"></span>\n }\n </span>\n </span>\n </button>\n\n @if (label() && labelPosition() === 'right') {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-switch.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/switch/switch.component.ts","../../../../libs/angular-ui-kit/src/lib/switch/switch.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n computed,\n forwardRef,\n input,\n linkedSignal,\n output,\n signal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nlet nextUniqueId = 0;\n\n/**\n * Switch component for boolean toggle functionality.\n *\n * Features:\n * - Color variants (primary, secondary, success, warning, danger)\n * - Size options (sm, md, lg)\n * - Loading state with spinner\n * - Configurable label positioning (left/right)\n * - Help text below the label, linked by `aria-describedby` (`helpText`, 3.16)\n * - Rows with a label or help text are at least 44 px high\n * (`--lc-switch-min-height`, default `var(--size-min-touch-height)`; `0`\n * restores the 3.15 rows of 21/24/28 px)\n * - Keyboard toggle support\n * - Disabled and required states\n * - ControlValueAccessor integration for reactive forms\n *\n * @example\n * ```html\n * <lc-switch label=\"Enable notifications\" [(ngModel)]=\"isEnabled\" />\n * ```\n */\n@Component({\n selector: 'lc-switch',\n standalone: true,\n imports: [],\n templateUrl: './switch.component.html',\n styleUrl: './switch.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SwitchComponent),\n multi: true,\n },\n ],\n})\nexport class SwitchComponent implements ControlValueAccessor {\n /**\n * Visual variant of the switch\n */\n readonly variant = input<'primary' | 'secondary' | 'success' | 'warning' | 'danger'>('primary');\n\n /**\n * Size of the switch\n */\n readonly size = input<'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the switch is checked (template-driven usage; forms write via `writeValue`)\n */\n readonly checked = input<boolean>(false);\n\n /**\n * Whether the switch is disabled\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the switch is required\n */\n readonly required = input<boolean>(false);\n\n /**\n * Whether the switch is in loading state\n */\n readonly loading = input<boolean>(false);\n\n /**\n * Label text\n */\n readonly label = input<string>('');\n\n /**\n * Label position relative to switch\n */\n readonly labelPosition = input<'left' | 'right'>('right');\n\n /**\n * ARIA label for accessibility (overrides the visible label as accessible name)\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Description below the label, linked to the switch by `aria-describedby`\n * (read after the name and state). Wraps; 14 px secondary text. A tap on it\n * toggles the switch like the label. Empty renders no element and no\n * `aria-describedby` (3.15).\n * @default ''\n */\n readonly helpText = input<string>('');\n\n /**\n * Emitted when checked state changes\n */\n readonly checkedChange = output<boolean>();\n\n /** Per-instance id so the visible `<label for>` points at this switch. */\n readonly switchId = `lc-switch-${nextUniqueId++}`;\n\n /** id of the rendered help text (only while `helpText` is set). */\n readonly helpTextId = computed(() => (this.helpText() ? `${this.switchId}-help` : null));\n\n /**\n * Effective checked state: follows the `checked` input, overridden by\n * form writes (`writeValue`) and user toggles.\n */\n readonly checkedState = linkedSignal(() => this.checked());\n\n // Internal disabled state (for ControlValueAccessor)\n private readonly formDisabled = signal<boolean>(false);\n\n /**\n * Computed disabled state from both input and form control\n */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /**\n * Computed classes for the switch element\n */\n readonly switchClasses = computed(() => {\n const classes = ['lc-switch', `lc-switch--${this.variant()}`, `lc-switch--${this.size()}`];\n\n if (this.checkedState()) {\n classes.push('lc-switch--checked');\n }\n\n if (this.isDisabled()) {\n classes.push('lc-switch--disabled');\n }\n\n if (this.loading()) {\n classes.push('lc-switch--loading');\n }\n\n return classes.join(' ');\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: boolean) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n /**\n * Toggle the switch state\n */\n toggle(): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n\n const next = !this.checkedState();\n this.checkedState.set(next);\n this.onChange(next);\n this.onTouched();\n this.checkedChange.emit(next);\n }\n\n /**\n * Handle click event (the control is a native button, so Space/Enter arrive here too)\n */\n onClick(): void {\n this.toggle();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: boolean | null | undefined): void {\n this.checkedState.set(!!value);\n }\n\n registerOnChange(fn: (value: boolean) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n","<div\n class=\"lc-switch-wrapper\"\n [class.lc-switch-wrapper--label-left]=\"labelPosition() === 'left'\"\n [class.lc-switch-wrapper--label-right]=\"labelPosition() === 'right'\"\n [class.lc-switch-wrapper--help]=\"!!helpTextId()\"\n>\n @if (labelPosition() === 'left') {\n @if (helpTextId(); as helpId) {\n <!-- Label and help text in one column; the label's ::after covers it, so a tap on the help toggles too. -->\n <div class=\"lc-switch-text lc-switch-text--{{ size() }}\">\n @if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n <div [id]=\"helpId\" class=\"lc-switch-help\">{{ helpText() }}</div>\n </div>\n } @else if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n }\n\n <!-- Native button: labelable (label for/id works), focusable and Space/Enter toggle it without custom key handling -->\n <button\n type=\"button\"\n [id]=\"switchId\"\n [class]=\"switchClasses()\"\n role=\"switch\"\n [attr.aria-checked]=\"checkedState()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"helpTextId()\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-busy]=\"loading() || null\"\n [attr.aria-disabled]=\"loading() && !isDisabled() ? true : null\"\n [disabled]=\"isDisabled()\"\n (click)=\"onClick()\"\n >\n <span class=\"lc-switch-track\">\n <span class=\"lc-switch-thumb\">\n @if (loading()) {\n <span class=\"lc-switch-loading-spinner\"></span>\n }\n </span>\n </span>\n </button>\n\n @if (labelPosition() === 'right') {\n @if (helpTextId(); as helpId) {\n <div class=\"lc-switch-text lc-switch-text--{{ size() }}\">\n @if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n <div [id]=\"helpId\" class=\"lc-switch-help\">{{ helpText() }}</div>\n </div>\n } @else if (label()) {\n <label [for]=\"switchId\" class=\"lc-switch-label\">{{ label() }}</label>\n }\n }\n</div>\n"],"mappings":"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"}
@@ -59,6 +59,14 @@ i0.ɵɵngDeclareClassMetadata({
59
59
  }]
60
60
  }] }
61
61
  });
62
+ const NESTED_CONTROL = "a[href], button, input, select, textarea, summary, label, [contenteditable], [tabindex]:not([tabindex=\"-1\"]), [role=button], [role=link], [role=switch], [role=checkbox], [role=menuitem], [role=tab], [role=option]";
63
+ function isFromNestedControl(event) {
64
+ const row = event.currentTarget;
65
+ const target = event.target;
66
+ if (!row || !target || typeof target.closest !== "function") return false;
67
+ const hit = target.closest(NESTED_CONTROL);
68
+ return !!hit && hit !== row && row.contains(hit);
69
+ }
62
70
  var TableComponent = class TableComponent {
63
71
  cellTemplates;
64
72
  columns = input([], ...ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []);
@@ -66,6 +74,9 @@ var TableComponent = class TableComponent {
66
74
  variant = input("default", ...ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []);
67
75
  size = input("md", ...ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []);
68
76
  hoverable = input(false, ...ngDevMode ? [{ debugName: "hoverable" }] : /* istanbul ignore next */ []);
77
+ clickable = input(false, ...ngDevMode ? [{ debugName: "clickable" }] : /* istanbul ignore next */ []);
78
+ caption = input("", ...ngDevMode ? [{ debugName: "caption" }] : /* istanbul ignore next */ []);
79
+ showCaption = input(true, ...ngDevMode ? [{ debugName: "showCaption" }] : /* istanbul ignore next */ []);
69
80
  responsive = input(true, ...ngDevMode ? [{ debugName: "responsive" }] : /* istanbul ignore next */ []);
70
81
  emptyText = input("No data available", ...ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []);
71
82
  filterPlaceholderLabel = input("Filter", ...ngDevMode ? [{ debugName: "filterPlaceholderLabel" }] : /* istanbul ignore next */ []);
@@ -340,6 +351,7 @@ var TableComponent = class TableComponent {
340
351
  classes.push(`lc-table--${this.variant()}`);
341
352
  classes.push(`lc-table--${this.size()}`);
342
353
  if (this.hoverable()) classes.push("lc-table--hoverable");
354
+ if (this.clickable()) classes.push("lc-table--clickable");
343
355
  return classes.join(" ");
344
356
  }, ...ngDevMode ? [{ debugName: "tableClasses" }] : /* istanbul ignore next */ []);
345
357
  wrapperClasses = computed(() => {
@@ -419,7 +431,8 @@ var TableComponent = class TableComponent {
419
431
  hasCustomTemplate(columnKey) {
420
432
  return !!this.getCellTemplate(columnKey);
421
433
  }
422
- onRowClick(row) {
434
+ onRowClick(row, event) {
435
+ if (this.clickable() && event && isFromNestedControl(event)) return;
423
436
  this.rowClick.emit(row);
424
437
  }
425
438
  hasActions = computed(() => this.actions().length > 0, ...ngDevMode ? [{ debugName: "hasActions" }] : /* istanbul ignore next */ []);
@@ -444,14 +457,20 @@ var TableComponent = class TableComponent {
444
457
  return rows.some((r) => r.key === key) ? key : rows[0]?.key;
445
458
  }, ...ngDevMode ? [{ debugName: "effectiveFocusedKey" }] : /* istanbul ignore next */ []);
446
459
  rowTabindex(dr) {
447
- if (!this.treeMode()) return null;
460
+ if (!this.treeMode()) return this.clickable() ? 0 : null;
448
461
  return dr.key === this.effectiveFocusedKey() ? 0 : -1;
449
462
  }
450
463
  onRowFocus(dr) {
451
464
  if (this.treeMode()) this.focusedRowKey.set(dr.key);
452
465
  }
453
466
  onRowKeydown(event, dr) {
454
- if (!this.treeMode()) return;
467
+ if (!this.treeMode()) {
468
+ if (!this.clickable() || event.target !== event.currentTarget) return;
469
+ if (event.key !== "Enter" && event.key !== " ") return;
470
+ event.preventDefault();
471
+ this.rowClick.emit(dr.row);
472
+ return;
473
+ }
455
474
  if (event.target.tagName === "INPUT") return;
456
475
  const rows = this.displayRows();
457
476
  const index = rows.findIndex((r) => r.key === dr.key);
@@ -696,6 +715,27 @@ var TableComponent = class TableComponent {
696
715
  isRequired: false,
697
716
  transformFunction: null
698
717
  },
718
+ clickable: {
719
+ classPropertyName: "clickable",
720
+ publicName: "clickable",
721
+ isSignal: true,
722
+ isRequired: false,
723
+ transformFunction: null
724
+ },
725
+ caption: {
726
+ classPropertyName: "caption",
727
+ publicName: "caption",
728
+ isSignal: true,
729
+ isRequired: false,
730
+ transformFunction: null
731
+ },
732
+ showCaption: {
733
+ classPropertyName: "showCaption",
734
+ publicName: "showCaption",
735
+ isSignal: true,
736
+ isRequired: false,
737
+ transformFunction: null
738
+ },
699
739
  responsive: {
700
740
  classPropertyName: "responsive",
701
741
  publicName: "responsive",
@@ -933,8 +973,8 @@ var TableComponent = class TableComponent {
933
973
  isSignal: true
934
974
  }],
935
975
  ngImport: i0,
936
- template: "<div [class]=\"wrapperClasses()\">\n <table [class]=\"tableClasses()\" [attr.role]=\"treeMode() ? 'treegrid' : 'table'\">\n <thead>\n <!-- Filter row -->\n @if (filterable()) {\n <tr class=\"lc-table__filter-row\">\n @if (selectable()) {\n <th class=\"lc-table__select-cell\"></th>\n }\n @for (column of columns(); track column.key) {\n <th>\n @if (column.filterable !== false) {\n <lc-input\n class=\"lc-table__filter-input\"\n size=\"xs\"\n [placeholder]=\"filterPlaceholderLabel() + ' ' + column.label\"\n [ariaLabel]=\"filterAriaLabel() + ' ' + column.label\"\n [ngModel]=\"getFilterValue(column.key)\"\n (ngModelChange)=\"onFilterChange(column.key, $event)\"\n />\n }\n </th>\n }\n @if (hasActions()) {\n <th class=\"lc-table__actions-cell\"></th>\n }\n </tr>\n }\n <!-- Header row -->\n <tr>\n @if (selectable()) {\n <th class=\"lc-table__select-cell\" scope=\"col\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"allSelected()\"\n [indeterminate]=\"someSelected()\"\n (change)=\"toggleSelectAll()\"\n [attr.aria-label]=\"selectAllAriaLabel()\"\n />\n </th>\n }\n @for (column of columns(); track column.key) {\n <!-- A sortable header is a keyboard stop too: Enter/Space sort like a click. -->\n <th\n scope=\"col\"\n [class]=\"getHeaderClasses(column) + (column.cssClass ? ' ' + column.cssClass : '')\"\n [attr.aria-sort]=\"getAriaSort(column.key)\"\n [attr.tabindex]=\"column.sortable ? 0 : null\"\n [style.width]=\"column.width\"\n [title]=\"column.tooltip || ''\"\n (click)=\"handleSort(column.key)\"\n (keydown)=\"column.sortable ? onHeaderKeydown($event, column.key) : null\">\n {{ column.label }}\n @if (column.sortable && getSortState(column.key)) {\n <span class=\"sort-indicator\" [attr.aria-label]=\"getSortState(column.key) === 'asc' ? sortedAscendingLabel() : sortedDescendingLabel()\">\n @if (getSortState(column.key) === 'asc') {\n ↑\n } @else {\n ↓\n }\n </span>\n }\n </th>\n }\n @if (hasActions()) {\n <th\n scope=\"col\"\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [style.width]=\"actionsWidth()\"\n >\n {{ actionsLabel() }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (displayRows().length === 0) {\n <tr>\n <td [attr.colspan]=\"columns().length + (selectable() ? 1 : 0) + (hasActions() ? 1 : 0)\" class=\"lc-table__empty\">\n {{ emptyText() }}\n </td>\n </tr>\n } @else {\n @for (dr of displayRows(); track dr.key) {\n <!-- In tree mode the rows form the treegrid's keyboard model: a roving\n tab stop plus Arrow/Home/End/Enter/Space handling (see the\n component). Flat mode keeps plain table rows. -->\n <tr\n #dataRow\n [class.lc-table__row--selected]=\"selectable() && isRowSelected(dr.row)\"\n [class.lc-table__row--group]=\"dr.hasChildren\"\n [attr.role]=\"treeMode() ? 'row' : null\"\n [attr.tabindex]=\"rowTabindex(dr)\"\n [attr.aria-level]=\"treeMode() ? dr.level : null\"\n [attr.aria-expanded]=\"treeMode() && dr.hasChildren ? dr.expanded : null\"\n [attr.aria-posinset]=\"treeMode() ? dr.posInSet : null\"\n [attr.aria-setsize]=\"treeMode() ? dr.setSize : null\"\n [attr.aria-selected]=\"treeMode() && selectable() ? isRowSelected(dr.row) : null\"\n (click)=\"onRowClick(dr.row)\"\n (focus)=\"onRowFocus(dr)\"\n (keydown)=\"onRowKeydown($event, dr)\"\n >\n @if (selectable()) {\n <td class=\"lc-table__select-cell\" [attr.role]=\"treeMode() ? 'gridcell' : null\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"isRowSelected(dr.row)\"\n (change)=\"toggleRowSelect(dr.row)\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"selectRowAriaLabel() + ' ' + (dr.absIndex + 1)\"\n />\n </td>\n }\n @for (column of columns(); track column.key) {\n <td\n [class]=\"getCellClasses(dr.row, column, dr.absIndex)\"\n [ngStyle]=\"getCellStyles(dr.row, column, dr.absIndex)\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (dblclick)=\"editable() ? startEdit(dr.row, column.key, getCellValue(dr.row, column.key)) : null\"\n >\n @if (isTreeColumn(column.key)) {\n <span class=\"lc-table__tree-cell\">\n @for (lvl of indentLevels(dr.depth); track lvl) {\n <span class=\"lc-table__tree-indent\" [style.width.px]=\"indentSize()\"></span>\n }\n <span class=\"lc-table__tree-chevron\" [style.width.px]=\"indentSize()\">\n @if (dr.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-table__chevron-btn\"\n [class.lc-table__chevron-btn--expanded]=\"dr.expanded\"\n [attr.aria-label]=\"(dr.expanded ? collapseRowAriaLabel() : expandRowAriaLabel()) + ' ' + treeCellLabel(dr.row)\"\n [attr.aria-expanded]=\"dr.expanded\"\n (click)=\"onChevronClick(dr, $event)\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n <span class=\"lc-table__tree-content\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n </span>\n </span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n }\n </td>\n }\n @if (hasActions()) {\n <td\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"lc-table__actions\">\n @for (action of actions(); track action.key) {\n @if (!isActionHidden(action, dr.row, dr.absIndex)) {\n <lc-button\n [variant]=\"action.variant ?? 'ghost'\"\n size=\"xs\"\n [iconOnly]=\"!action.label\"\n [disabled]=\"isActionDisabled(action, dr.row, dr.absIndex)\"\n [ariaLabel]=\"action.tooltip ?? action.label ?? action.key\"\n (clicked)=\"onActionClick(action, dr.row, dr.absIndex)\"\n >\n @if (action.icon) {\n <lc-icon [name]=\"action.icon\" size=\"sm\" [decorative]=\"true\" />\n }\n @if (action.label) {\n {{ action.label }}\n }\n </lc-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n<!-- Shared cell content (edit input / custom template / formatted value) -->\n<ng-template #cellContent let-row=\"row\" let-column=\"column\" let-absIndex=\"absIndex\">\n @if (isEditing(row, column.key)) {\n <input\n #editInput\n type=\"text\"\n class=\"lc-table__edit-input\"\n [value]=\"editValue()\"\n (input)=\"editValue.set(getInputValue($event))\"\n (blur)=\"commitEdit(row, column.key)\"\n (keydown)=\"onEditKeydown($event, row, column.key)\"\n />\n } @else if (hasCustomTemplate(column.key)) {\n <ng-container *ngTemplateOutlet=\"getCellTemplate(column.key)!.template; context: { $implicit: row }\"></ng-container>\n } @else {\n {{ getFormattedCellValue(row, column, absIndex) }}\n }\n</ng-template>\n\n<!-- Pagination -->\n@if (paginate()) {\n <div class=\"lc-table-pagination\">\n <div class=\"lc-table-pagination__info\">\n {{ paginationStart }}–{{ paginationEnd }} {{ rangeOfLabel() }} {{ totalRows() }}\n </div>\n <div class=\"lc-table-pagination__controls\">\n <lc-select\n class=\"lc-table-pagination__size\"\n [size]=\"size() === 'sm' ? 'sm' : size() === 'lg' ? 'lg' : 'md'\"\n [options]=\"pageSizeSelectOptions()\"\n [ngModel]=\"internalPageSize()\"\n [ariaLabel]=\"rowsPerPageAriaLabel()\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() === 0\"\n (click)=\"goToPage(currentPage() - 1)\"\n [attr.aria-label]=\"previousPageAriaLabel()\"\n >‹</button>\n <span class=\"lc-table-pagination__page\">\n {{ currentPage() + 1 }} / {{ totalPages() }}\n </span>\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() >= totalPages() - 1\"\n (click)=\"goToPage(currentPage() + 1)\"\n [attr.aria-label]=\"nextPageAriaLabel()\"\n >›</button>\n </div>\n </div>\n}\n",
937
- styles: [".lc-table-wrapper{width:100%}.lc-table-wrapper--responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:768px){.lc-table-wrapper--responsive::-webkit-scrollbar{height:8px}.lc-table-wrapper--responsive::-webkit-scrollbar-thumb{background-color:var(--color-border);border-radius:var(--border-radius-sm)}}.lc-table{width:100%;border-collapse:collapse;border-spacing:0;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--typography-font-family)}.lc-table thead{background-color:var(--color-surface-2);border-bottom:2px solid var(--color-border-strong)}.lc-table thead tr th{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));text-align:left;font-weight:var(--typography-font-weight-semibold);font-size:var(--typography-font-size-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-secondary);white-space:nowrap;-webkit-user-select:none;user-select:none}.lc-table thead tr th.sortable{cursor:pointer;position:relative;padding-right:var(--spacing-8);transition:background-color .2s ease}.lc-table thead tr th.sortable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-table thead tr th.sortable:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table thead tr th.sortable .sort-indicator{position:absolute;right:var(--spacing-3);top:50%;transform:translateY(-50%);font-size:var(--typography-font-size-xs);color:var(--color-primary)}.lc-table thead tr th.sorted-asc,.lc-table thead tr th.sorted-desc{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table tbody tr{border-bottom:1px solid var(--color-divider)}.lc-table tbody tr:last-child{border-bottom:none}.lc-table tbody tr td{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));font-size:var(--typography-font-size-sm);color:var(--color-text-primary)}.lc-table__empty{text-align:center;padding:var(--spacing-8) var(--spacing-4)!important;color:var(--color-text-secondary);font-style:italic}.lc-table--striped tbody tr:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-table--striped tbody tr:nth-child(2n){background-color:var(--color-surface-2)}.lc-table--bordered{border:1px solid var(--color-border)}.lc-table--bordered thead tr th,.lc-table--bordered tbody tr td{border-right:1px solid var(--color-divider)}.lc-table--bordered thead tr th:last-child,.lc-table--bordered tbody tr td:last-child{border-right:none}.lc-table--bordered tbody tr{border-bottom:1px solid var(--color-border)}.lc-table--hoverable tbody tr{transition:background-color .15s ease;cursor:pointer}.lc-table--hoverable tbody tr:hover{background-color:var(--color-surface-hover)}.lc-table--sm thead tr th,.lc-table--sm tbody tr td{padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--typography-font-size-xs)}.lc-table--lg thead tr th,.lc-table--lg tbody tr td{padding:var(--lc-density-padding-md, var(--spacing-4)) var(--spacing-5);font-size:var(--typography-font-size-base)}.lc-table__filter-row th{padding:var(--spacing-2) var(--spacing-4)!important;background-color:var(--color-surface)!important;border-bottom:1px solid var(--color-divider)!important}.lc-table__filter-input{width:100%;display:block}.lc-table__filter-input .input-field{font-size:var(--typography-font-size-xs)}.lc-table__select-cell{width:40px;text-align:center!important;padding:var(--spacing-2)!important}.lc-table__checkbox{width:16px;height:16px;cursor:pointer;accent-color:var(--color-primary)}.lc-table__row--selected{background-color:var(--color-surface-selected)!important}.lc-table__actions-cell{white-space:nowrap}.lc-table th.lc-table__actions-cell--start,.lc-table td.lc-table__actions-cell--start{text-align:left}.lc-table th.lc-table__actions-cell--center,.lc-table td.lc-table__actions-cell--center{text-align:center}.lc-table th.lc-table__actions-cell--end,.lc-table td.lc-table__actions-cell--end{text-align:right}.lc-table__actions{display:flex;align-items:center;gap:var(--spacing-2);justify-content:flex-start}.lc-table__actions-cell--center .lc-table__actions{justify-content:center}.lc-table__actions-cell--end .lc-table__actions{justify-content:flex-end}.lc-table__tree-cell{display:flex;align-items:center;min-width:0}.lc-table__tree-indent{flex:0 0 auto;align-self:stretch;position:relative}.lc-table__tree-indent:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background-color:var(--color-border)}.lc-table__tree-chevron{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center}.lc-table__chevron-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--border-radius-sm);transition:transform .2s ease,background-color .15s ease,color .15s ease}.lc-table__chevron-btn:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table__chevron-btn:focus-visible{outline:2px solid var(--color-primary);outline-offset:1px}.lc-table__chevron-btn--expanded{transform:rotate(90deg)}.lc-table__tree-content{flex:1 1 auto;min-width:0}.lc-table__row--group .lc-table__tree-content{font-weight:var(--typography-font-weight-semibold)}.lc-table__edit-input{width:100%;padding:var(--spacing-1) var(--spacing-2);border:2px solid var(--color-primary);border-radius:var(--border-radius-sm);font-size:inherit;background-color:var(--color-surface-2);color:var(--color-text-primary);outline:none}.lc-table-pagination{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-1);font-size:var(--typography-font-size-sm);color:var(--color-text-secondary)}.lc-table-pagination__info{white-space:nowrap}.lc-table-pagination__controls{display:flex;align-items:center;gap:var(--spacing-2)}.lc-table-pagination__size{display:block;min-width:110px}.lc-table-pagination__btn{appearance:none;border:1px solid var(--color-border);background-color:var(--color-surface);color:var(--color-text-primary);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sm);font-size:var(--typography-font-size-base);cursor:pointer;transition:background-color .15s ease}.lc-table-pagination__btn:hover:not(:disabled){background-color:var(--color-surface-hover)}.lc-table-pagination__btn:disabled{opacity:.4;cursor:not-allowed}.lc-table-pagination__page{white-space:nowrap;min-width:60px;text-align:center}@media(max-width:640px){.lc-table-wrapper--stack .lc-table thead{display:none}.lc-table-wrapper--stack .lc-table tbody tr{display:block;margin-bottom:var(--spacing-4);border:1px solid var(--color-border);border-radius:var(--border-radius-md)}.lc-table-wrapper--stack .lc-table tbody tr td{display:block;text-align:right;padding:var(--spacing-2) var(--spacing-4);border-bottom:1px solid var(--color-divider)}.lc-table-wrapper--stack .lc-table tbody tr td:last-child{border-bottom:none}.lc-table-wrapper--stack .lc-table tbody tr td:before{content:attr(data-label);float:left;font-weight:var(--typography-font-weight-semibold);color:var(--color-text-secondary)}}\n"],
976
+ template: "<div [class]=\"wrapperClasses()\">\n <table [class]=\"tableClasses()\" [attr.role]=\"treeMode() ? 'treegrid' : 'table'\">\n @if (caption()) {\n <!-- First child of the table, so it names the table (and the treegrid). -->\n <caption class=\"lc-table__caption\" [class.lc-table__caption--hidden]=\"!showCaption()\">{{ caption() }}</caption>\n }\n <thead>\n <!-- Filter row -->\n @if (filterable()) {\n <tr class=\"lc-table__filter-row\">\n @if (selectable()) {\n <th class=\"lc-table__select-cell\"></th>\n }\n @for (column of columns(); track column.key) {\n <th>\n @if (column.filterable !== false) {\n <lc-input\n class=\"lc-table__filter-input\"\n size=\"xs\"\n [placeholder]=\"filterPlaceholderLabel() + ' ' + column.label\"\n [ariaLabel]=\"filterAriaLabel() + ' ' + column.label\"\n [ngModel]=\"getFilterValue(column.key)\"\n (ngModelChange)=\"onFilterChange(column.key, $event)\"\n />\n }\n </th>\n }\n @if (hasActions()) {\n <th class=\"lc-table__actions-cell\"></th>\n }\n </tr>\n }\n <!-- Header row -->\n <tr>\n @if (selectable()) {\n <th class=\"lc-table__select-cell\" scope=\"col\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"allSelected()\"\n [indeterminate]=\"someSelected()\"\n (change)=\"toggleSelectAll()\"\n [attr.aria-label]=\"selectAllAriaLabel()\"\n />\n </th>\n }\n @for (column of columns(); track column.key) {\n <!-- A sortable header is a keyboard stop too: Enter/Space sort like a click. -->\n <th\n scope=\"col\"\n [class]=\"getHeaderClasses(column) + (column.cssClass ? ' ' + column.cssClass : '')\"\n [attr.aria-sort]=\"getAriaSort(column.key)\"\n [attr.tabindex]=\"column.sortable ? 0 : null\"\n [style.width]=\"column.width\"\n [title]=\"column.tooltip || ''\"\n (click)=\"handleSort(column.key)\"\n (keydown)=\"column.sortable ? onHeaderKeydown($event, column.key) : null\">\n {{ column.label }}\n @if (column.sortable && getSortState(column.key)) {\n <span class=\"sort-indicator\" [attr.aria-label]=\"getSortState(column.key) === 'asc' ? sortedAscendingLabel() : sortedDescendingLabel()\">\n @if (getSortState(column.key) === 'asc') {\n ↑\n } @else {\n ↓\n }\n </span>\n }\n </th>\n }\n @if (hasActions()) {\n <th\n scope=\"col\"\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [style.width]=\"actionsWidth()\"\n >\n {{ actionsLabel() }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (displayRows().length === 0) {\n <tr>\n <td [attr.colspan]=\"columns().length + (selectable() ? 1 : 0) + (hasActions() ? 1 : 0)\" class=\"lc-table__empty\">\n {{ emptyText() }}\n </td>\n </tr>\n } @else {\n @for (dr of displayRows(); track dr.key) {\n <!-- In tree mode the rows form the treegrid's keyboard model: a roving\n tab stop plus Arrow/Home/End/Enter/Space handling (see the\n component). Flat mode keeps plain table rows. -->\n <tr\n #dataRow\n [class.lc-table__row--selected]=\"selectable() && isRowSelected(dr.row)\"\n [class.lc-table__row--group]=\"dr.hasChildren\"\n [attr.role]=\"treeMode() ? 'row' : null\"\n [attr.tabindex]=\"rowTabindex(dr)\"\n [attr.aria-level]=\"treeMode() ? dr.level : null\"\n [attr.aria-expanded]=\"treeMode() && dr.hasChildren ? dr.expanded : null\"\n [attr.aria-posinset]=\"treeMode() ? dr.posInSet : null\"\n [attr.aria-setsize]=\"treeMode() ? dr.setSize : null\"\n [attr.aria-selected]=\"treeMode() && selectable() ? isRowSelected(dr.row) : null\"\n (click)=\"onRowClick(dr.row, $event)\"\n (focus)=\"onRowFocus(dr)\"\n (keydown)=\"onRowKeydown($event, dr)\"\n >\n @if (selectable()) {\n <td class=\"lc-table__select-cell\" [attr.role]=\"treeMode() ? 'gridcell' : null\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"isRowSelected(dr.row)\"\n (change)=\"toggleRowSelect(dr.row)\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"selectRowAriaLabel() + ' ' + (dr.absIndex + 1)\"\n />\n </td>\n }\n @for (column of columns(); track column.key) {\n <td\n [class]=\"getCellClasses(dr.row, column, dr.absIndex)\"\n [ngStyle]=\"getCellStyles(dr.row, column, dr.absIndex)\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (dblclick)=\"editable() ? startEdit(dr.row, column.key, getCellValue(dr.row, column.key)) : null\"\n >\n @if (isTreeColumn(column.key)) {\n <span class=\"lc-table__tree-cell\">\n @for (lvl of indentLevels(dr.depth); track lvl) {\n <span class=\"lc-table__tree-indent\" [style.width.px]=\"indentSize()\"></span>\n }\n <span class=\"lc-table__tree-chevron\" [style.width.px]=\"indentSize()\">\n @if (dr.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-table__chevron-btn\"\n [class.lc-table__chevron-btn--expanded]=\"dr.expanded\"\n [attr.aria-label]=\"(dr.expanded ? collapseRowAriaLabel() : expandRowAriaLabel()) + ' ' + treeCellLabel(dr.row)\"\n [attr.aria-expanded]=\"dr.expanded\"\n (click)=\"onChevronClick(dr, $event)\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n <span class=\"lc-table__tree-content\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n </span>\n </span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n }\n </td>\n }\n @if (hasActions()) {\n <td\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"lc-table__actions\">\n @for (action of actions(); track action.key) {\n @if (!isActionHidden(action, dr.row, dr.absIndex)) {\n <lc-button\n [variant]=\"action.variant ?? 'ghost'\"\n size=\"xs\"\n [iconOnly]=\"!action.label\"\n [disabled]=\"isActionDisabled(action, dr.row, dr.absIndex)\"\n [ariaLabel]=\"action.tooltip ?? action.label ?? action.key\"\n (clicked)=\"onActionClick(action, dr.row, dr.absIndex)\"\n >\n @if (action.icon) {\n <lc-icon [name]=\"action.icon\" size=\"sm\" [decorative]=\"true\" />\n }\n @if (action.label) {\n {{ action.label }}\n }\n </lc-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n<!-- Shared cell content (edit input / custom template / formatted value) -->\n<ng-template #cellContent let-row=\"row\" let-column=\"column\" let-absIndex=\"absIndex\">\n @if (isEditing(row, column.key)) {\n <input\n #editInput\n type=\"text\"\n class=\"lc-table__edit-input\"\n [value]=\"editValue()\"\n (input)=\"editValue.set(getInputValue($event))\"\n (blur)=\"commitEdit(row, column.key)\"\n (keydown)=\"onEditKeydown($event, row, column.key)\"\n />\n } @else if (hasCustomTemplate(column.key)) {\n <ng-container *ngTemplateOutlet=\"getCellTemplate(column.key)!.template; context: { $implicit: row }\"></ng-container>\n } @else {\n {{ getFormattedCellValue(row, column, absIndex) }}\n }\n</ng-template>\n\n<!-- Pagination -->\n@if (paginate()) {\n <div class=\"lc-table-pagination\">\n <div class=\"lc-table-pagination__info\">\n {{ paginationStart }}–{{ paginationEnd }} {{ rangeOfLabel() }} {{ totalRows() }}\n </div>\n <div class=\"lc-table-pagination__controls\">\n <lc-select\n class=\"lc-table-pagination__size\"\n [size]=\"size() === 'sm' ? 'sm' : size() === 'lg' ? 'lg' : 'md'\"\n [options]=\"pageSizeSelectOptions()\"\n [ngModel]=\"internalPageSize()\"\n [ariaLabel]=\"rowsPerPageAriaLabel()\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() === 0\"\n (click)=\"goToPage(currentPage() - 1)\"\n [attr.aria-label]=\"previousPageAriaLabel()\"\n >‹</button>\n <span class=\"lc-table-pagination__page\">\n {{ currentPage() + 1 }} / {{ totalPages() }}\n </span>\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() >= totalPages() - 1\"\n (click)=\"goToPage(currentPage() + 1)\"\n [attr.aria-label]=\"nextPageAriaLabel()\"\n >›</button>\n </div>\n </div>\n}\n",
977
+ styles: [".lc-table-wrapper{width:100%}.lc-table-wrapper--responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:768px){.lc-table-wrapper--responsive::-webkit-scrollbar{height:8px}.lc-table-wrapper--responsive::-webkit-scrollbar-thumb{background-color:var(--color-border);border-radius:var(--border-radius-sm)}}.lc-table{width:100%;border-collapse:collapse;border-spacing:0;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--typography-font-family)}.lc-table thead{background-color:var(--color-surface-2);border-bottom:2px solid var(--color-border-strong)}.lc-table thead tr th{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));text-align:left;font-weight:var(--typography-font-weight-semibold);font-size:var(--typography-font-size-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-secondary);white-space:nowrap;-webkit-user-select:none;user-select:none}.lc-table thead tr th.sortable{cursor:pointer;position:relative;padding-right:var(--spacing-8);transition:background-color .2s ease}.lc-table thead tr th.sortable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-table thead tr th.sortable:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table thead tr th.sortable .sort-indicator{position:absolute;right:var(--spacing-3);top:50%;transform:translateY(-50%);font-size:var(--typography-font-size-xs);color:var(--color-primary)}.lc-table thead tr th.sorted-asc,.lc-table thead tr th.sorted-desc{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table tbody tr{border-bottom:1px solid var(--color-divider)}.lc-table tbody tr:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-table tbody tr:last-child{border-bottom:none}.lc-table tbody tr td{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));font-size:var(--typography-font-size-sm);color:var(--color-text-primary)}.lc-table__empty{text-align:center;padding:var(--spacing-8) var(--spacing-4)!important;color:var(--color-text-secondary);font-style:italic}.lc-table--striped tbody tr:nth-child(2n){background-color:var(--color-surface-2)}.lc-table--bordered{border:1px solid var(--color-border)}.lc-table--bordered thead tr th,.lc-table--bordered tbody tr td{border-right:1px solid var(--color-divider)}.lc-table--bordered thead tr th:last-child,.lc-table--bordered tbody tr td:last-child{border-right:none}.lc-table--bordered tbody tr{border-bottom:1px solid var(--color-border)}.lc-table--hoverable tbody tr,.lc-table--clickable tbody tr{transition:background-color .15s ease;cursor:pointer}.lc-table--hoverable tbody tr:hover,.lc-table--clickable tbody tr:hover{background-color:var(--color-surface-hover)}.lc-table--sm thead tr th,.lc-table--sm tbody tr td{padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--typography-font-size-xs)}.lc-table--lg thead tr th,.lc-table--lg tbody tr td{padding:var(--lc-density-padding-md, var(--spacing-4)) var(--spacing-5);font-size:var(--typography-font-size-base)}.lc-table__caption{caption-side:top;padding:0 0 var(--spacing-3);text-align:start;font-family:var(--typography-font-family);font-size:var(--typography-font-size-base);font-weight:var(--typography-font-weight-semibold);line-height:var(--typography-line-height-normal);color:var(--color-text-primary)}.lc-table__caption--hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(forced-colors:active){.lc-table tbody tr:focus-visible{outline:2px solid Highlight}}.lc-table__filter-row th{padding:var(--spacing-2) var(--spacing-4)!important;background-color:var(--color-surface)!important;border-bottom:1px solid var(--color-divider)!important}.lc-table__filter-input{width:100%;display:block}.lc-table__filter-input .input-field{font-size:var(--typography-font-size-xs)}.lc-table__select-cell{width:40px;text-align:center!important;padding:var(--spacing-2)!important}.lc-table__checkbox{width:16px;height:16px;cursor:pointer;accent-color:var(--color-primary)}.lc-table__row--selected{background-color:var(--color-surface-selected)!important}.lc-table__actions-cell{white-space:nowrap}.lc-table th.lc-table__actions-cell--start,.lc-table td.lc-table__actions-cell--start{text-align:left}.lc-table th.lc-table__actions-cell--center,.lc-table td.lc-table__actions-cell--center{text-align:center}.lc-table th.lc-table__actions-cell--end,.lc-table td.lc-table__actions-cell--end{text-align:right}.lc-table__actions{display:flex;align-items:center;gap:var(--spacing-2);justify-content:flex-start}.lc-table__actions-cell--center .lc-table__actions{justify-content:center}.lc-table__actions-cell--end .lc-table__actions{justify-content:flex-end}.lc-table__tree-cell{display:flex;align-items:center;min-width:0}.lc-table__tree-indent{flex:0 0 auto;align-self:stretch;position:relative}.lc-table__tree-indent:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background-color:var(--color-border)}.lc-table__tree-chevron{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center}.lc-table__chevron-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--border-radius-sm);transition:transform .2s ease,background-color .15s ease,color .15s ease}.lc-table__chevron-btn:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table__chevron-btn:focus-visible{outline:2px solid var(--color-primary);outline-offset:1px}.lc-table__chevron-btn--expanded{transform:rotate(90deg)}.lc-table__tree-content{flex:1 1 auto;min-width:0}.lc-table__row--group .lc-table__tree-content{font-weight:var(--typography-font-weight-semibold)}.lc-table__edit-input{width:100%;padding:var(--spacing-1) var(--spacing-2);border:2px solid var(--color-primary);border-radius:var(--border-radius-sm);font-size:inherit;background-color:var(--color-surface-2);color:var(--color-text-primary);outline:none}.lc-table-pagination{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-1);font-size:var(--typography-font-size-sm);color:var(--color-text-secondary)}.lc-table-pagination__info{white-space:nowrap}.lc-table-pagination__controls{display:flex;align-items:center;gap:var(--spacing-2)}.lc-table-pagination__size{display:block;min-width:110px}.lc-table-pagination__btn{appearance:none;border:1px solid var(--color-border);background-color:var(--color-surface);color:var(--color-text-primary);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sm);font-size:var(--typography-font-size-base);cursor:pointer;transition:background-color .15s ease}.lc-table-pagination__btn:hover:not(:disabled){background-color:var(--color-surface-hover)}.lc-table-pagination__btn:disabled{opacity:.4;cursor:not-allowed}.lc-table-pagination__page{white-space:nowrap;min-width:60px;text-align:center}@media(max-width:640px){.lc-table-wrapper--stack .lc-table thead{display:none}.lc-table-wrapper--stack .lc-table tbody tr{display:block;margin-bottom:var(--spacing-4);border:1px solid var(--color-border);border-radius:var(--border-radius-md)}.lc-table-wrapper--stack .lc-table tbody tr td{display:block;text-align:right;padding:var(--spacing-2) var(--spacing-4);border-bottom:1px solid var(--color-divider)}.lc-table-wrapper--stack .lc-table tbody tr td:last-child{border-bottom:none}.lc-table-wrapper--stack .lc-table tbody tr td:before{content:attr(data-label);float:left;font-weight:var(--typography-font-weight-semibold);color:var(--color-text-secondary)}}\n"],
938
978
  dependencies: [
939
979
  {
940
980
  kind: "directive",
@@ -1108,8 +1148,8 @@ i0.ɵɵngDeclareClassMetadata({
1108
1148
  ],
1109
1149
  viewProviders: [ɵprovideKitIcons([ICON_CHEVRON_RIGHT])],
1110
1150
  changeDetection: ChangeDetectionStrategy.OnPush,
1111
- template: "<div [class]=\"wrapperClasses()\">\n <table [class]=\"tableClasses()\" [attr.role]=\"treeMode() ? 'treegrid' : 'table'\">\n <thead>\n <!-- Filter row -->\n @if (filterable()) {\n <tr class=\"lc-table__filter-row\">\n @if (selectable()) {\n <th class=\"lc-table__select-cell\"></th>\n }\n @for (column of columns(); track column.key) {\n <th>\n @if (column.filterable !== false) {\n <lc-input\n class=\"lc-table__filter-input\"\n size=\"xs\"\n [placeholder]=\"filterPlaceholderLabel() + ' ' + column.label\"\n [ariaLabel]=\"filterAriaLabel() + ' ' + column.label\"\n [ngModel]=\"getFilterValue(column.key)\"\n (ngModelChange)=\"onFilterChange(column.key, $event)\"\n />\n }\n </th>\n }\n @if (hasActions()) {\n <th class=\"lc-table__actions-cell\"></th>\n }\n </tr>\n }\n <!-- Header row -->\n <tr>\n @if (selectable()) {\n <th class=\"lc-table__select-cell\" scope=\"col\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"allSelected()\"\n [indeterminate]=\"someSelected()\"\n (change)=\"toggleSelectAll()\"\n [attr.aria-label]=\"selectAllAriaLabel()\"\n />\n </th>\n }\n @for (column of columns(); track column.key) {\n <!-- A sortable header is a keyboard stop too: Enter/Space sort like a click. -->\n <th\n scope=\"col\"\n [class]=\"getHeaderClasses(column) + (column.cssClass ? ' ' + column.cssClass : '')\"\n [attr.aria-sort]=\"getAriaSort(column.key)\"\n [attr.tabindex]=\"column.sortable ? 0 : null\"\n [style.width]=\"column.width\"\n [title]=\"column.tooltip || ''\"\n (click)=\"handleSort(column.key)\"\n (keydown)=\"column.sortable ? onHeaderKeydown($event, column.key) : null\">\n {{ column.label }}\n @if (column.sortable && getSortState(column.key)) {\n <span class=\"sort-indicator\" [attr.aria-label]=\"getSortState(column.key) === 'asc' ? sortedAscendingLabel() : sortedDescendingLabel()\">\n @if (getSortState(column.key) === 'asc') {\n ↑\n } @else {\n ↓\n }\n </span>\n }\n </th>\n }\n @if (hasActions()) {\n <th\n scope=\"col\"\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [style.width]=\"actionsWidth()\"\n >\n {{ actionsLabel() }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (displayRows().length === 0) {\n <tr>\n <td [attr.colspan]=\"columns().length + (selectable() ? 1 : 0) + (hasActions() ? 1 : 0)\" class=\"lc-table__empty\">\n {{ emptyText() }}\n </td>\n </tr>\n } @else {\n @for (dr of displayRows(); track dr.key) {\n <!-- In tree mode the rows form the treegrid's keyboard model: a roving\n tab stop plus Arrow/Home/End/Enter/Space handling (see the\n component). Flat mode keeps plain table rows. -->\n <tr\n #dataRow\n [class.lc-table__row--selected]=\"selectable() && isRowSelected(dr.row)\"\n [class.lc-table__row--group]=\"dr.hasChildren\"\n [attr.role]=\"treeMode() ? 'row' : null\"\n [attr.tabindex]=\"rowTabindex(dr)\"\n [attr.aria-level]=\"treeMode() ? dr.level : null\"\n [attr.aria-expanded]=\"treeMode() && dr.hasChildren ? dr.expanded : null\"\n [attr.aria-posinset]=\"treeMode() ? dr.posInSet : null\"\n [attr.aria-setsize]=\"treeMode() ? dr.setSize : null\"\n [attr.aria-selected]=\"treeMode() && selectable() ? isRowSelected(dr.row) : null\"\n (click)=\"onRowClick(dr.row)\"\n (focus)=\"onRowFocus(dr)\"\n (keydown)=\"onRowKeydown($event, dr)\"\n >\n @if (selectable()) {\n <td class=\"lc-table__select-cell\" [attr.role]=\"treeMode() ? 'gridcell' : null\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"isRowSelected(dr.row)\"\n (change)=\"toggleRowSelect(dr.row)\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"selectRowAriaLabel() + ' ' + (dr.absIndex + 1)\"\n />\n </td>\n }\n @for (column of columns(); track column.key) {\n <td\n [class]=\"getCellClasses(dr.row, column, dr.absIndex)\"\n [ngStyle]=\"getCellStyles(dr.row, column, dr.absIndex)\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (dblclick)=\"editable() ? startEdit(dr.row, column.key, getCellValue(dr.row, column.key)) : null\"\n >\n @if (isTreeColumn(column.key)) {\n <span class=\"lc-table__tree-cell\">\n @for (lvl of indentLevels(dr.depth); track lvl) {\n <span class=\"lc-table__tree-indent\" [style.width.px]=\"indentSize()\"></span>\n }\n <span class=\"lc-table__tree-chevron\" [style.width.px]=\"indentSize()\">\n @if (dr.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-table__chevron-btn\"\n [class.lc-table__chevron-btn--expanded]=\"dr.expanded\"\n [attr.aria-label]=\"(dr.expanded ? collapseRowAriaLabel() : expandRowAriaLabel()) + ' ' + treeCellLabel(dr.row)\"\n [attr.aria-expanded]=\"dr.expanded\"\n (click)=\"onChevronClick(dr, $event)\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n <span class=\"lc-table__tree-content\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n </span>\n </span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n }\n </td>\n }\n @if (hasActions()) {\n <td\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"lc-table__actions\">\n @for (action of actions(); track action.key) {\n @if (!isActionHidden(action, dr.row, dr.absIndex)) {\n <lc-button\n [variant]=\"action.variant ?? 'ghost'\"\n size=\"xs\"\n [iconOnly]=\"!action.label\"\n [disabled]=\"isActionDisabled(action, dr.row, dr.absIndex)\"\n [ariaLabel]=\"action.tooltip ?? action.label ?? action.key\"\n (clicked)=\"onActionClick(action, dr.row, dr.absIndex)\"\n >\n @if (action.icon) {\n <lc-icon [name]=\"action.icon\" size=\"sm\" [decorative]=\"true\" />\n }\n @if (action.label) {\n {{ action.label }}\n }\n </lc-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n<!-- Shared cell content (edit input / custom template / formatted value) -->\n<ng-template #cellContent let-row=\"row\" let-column=\"column\" let-absIndex=\"absIndex\">\n @if (isEditing(row, column.key)) {\n <input\n #editInput\n type=\"text\"\n class=\"lc-table__edit-input\"\n [value]=\"editValue()\"\n (input)=\"editValue.set(getInputValue($event))\"\n (blur)=\"commitEdit(row, column.key)\"\n (keydown)=\"onEditKeydown($event, row, column.key)\"\n />\n } @else if (hasCustomTemplate(column.key)) {\n <ng-container *ngTemplateOutlet=\"getCellTemplate(column.key)!.template; context: { $implicit: row }\"></ng-container>\n } @else {\n {{ getFormattedCellValue(row, column, absIndex) }}\n }\n</ng-template>\n\n<!-- Pagination -->\n@if (paginate()) {\n <div class=\"lc-table-pagination\">\n <div class=\"lc-table-pagination__info\">\n {{ paginationStart }}–{{ paginationEnd }} {{ rangeOfLabel() }} {{ totalRows() }}\n </div>\n <div class=\"lc-table-pagination__controls\">\n <lc-select\n class=\"lc-table-pagination__size\"\n [size]=\"size() === 'sm' ? 'sm' : size() === 'lg' ? 'lg' : 'md'\"\n [options]=\"pageSizeSelectOptions()\"\n [ngModel]=\"internalPageSize()\"\n [ariaLabel]=\"rowsPerPageAriaLabel()\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() === 0\"\n (click)=\"goToPage(currentPage() - 1)\"\n [attr.aria-label]=\"previousPageAriaLabel()\"\n >‹</button>\n <span class=\"lc-table-pagination__page\">\n {{ currentPage() + 1 }} / {{ totalPages() }}\n </span>\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() >= totalPages() - 1\"\n (click)=\"goToPage(currentPage() + 1)\"\n [attr.aria-label]=\"nextPageAriaLabel()\"\n >›</button>\n </div>\n </div>\n}\n",
1112
- styles: [".lc-table-wrapper{width:100%}.lc-table-wrapper--responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:768px){.lc-table-wrapper--responsive::-webkit-scrollbar{height:8px}.lc-table-wrapper--responsive::-webkit-scrollbar-thumb{background-color:var(--color-border);border-radius:var(--border-radius-sm)}}.lc-table{width:100%;border-collapse:collapse;border-spacing:0;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--typography-font-family)}.lc-table thead{background-color:var(--color-surface-2);border-bottom:2px solid var(--color-border-strong)}.lc-table thead tr th{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));text-align:left;font-weight:var(--typography-font-weight-semibold);font-size:var(--typography-font-size-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-secondary);white-space:nowrap;-webkit-user-select:none;user-select:none}.lc-table thead tr th.sortable{cursor:pointer;position:relative;padding-right:var(--spacing-8);transition:background-color .2s ease}.lc-table thead tr th.sortable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-table thead tr th.sortable:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table thead tr th.sortable .sort-indicator{position:absolute;right:var(--spacing-3);top:50%;transform:translateY(-50%);font-size:var(--typography-font-size-xs);color:var(--color-primary)}.lc-table thead tr th.sorted-asc,.lc-table thead tr th.sorted-desc{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table tbody tr{border-bottom:1px solid var(--color-divider)}.lc-table tbody tr:last-child{border-bottom:none}.lc-table tbody tr td{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));font-size:var(--typography-font-size-sm);color:var(--color-text-primary)}.lc-table__empty{text-align:center;padding:var(--spacing-8) var(--spacing-4)!important;color:var(--color-text-secondary);font-style:italic}.lc-table--striped tbody tr:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-table--striped tbody tr:nth-child(2n){background-color:var(--color-surface-2)}.lc-table--bordered{border:1px solid var(--color-border)}.lc-table--bordered thead tr th,.lc-table--bordered tbody tr td{border-right:1px solid var(--color-divider)}.lc-table--bordered thead tr th:last-child,.lc-table--bordered tbody tr td:last-child{border-right:none}.lc-table--bordered tbody tr{border-bottom:1px solid var(--color-border)}.lc-table--hoverable tbody tr{transition:background-color .15s ease;cursor:pointer}.lc-table--hoverable tbody tr:hover{background-color:var(--color-surface-hover)}.lc-table--sm thead tr th,.lc-table--sm tbody tr td{padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--typography-font-size-xs)}.lc-table--lg thead tr th,.lc-table--lg tbody tr td{padding:var(--lc-density-padding-md, var(--spacing-4)) var(--spacing-5);font-size:var(--typography-font-size-base)}.lc-table__filter-row th{padding:var(--spacing-2) var(--spacing-4)!important;background-color:var(--color-surface)!important;border-bottom:1px solid var(--color-divider)!important}.lc-table__filter-input{width:100%;display:block}.lc-table__filter-input .input-field{font-size:var(--typography-font-size-xs)}.lc-table__select-cell{width:40px;text-align:center!important;padding:var(--spacing-2)!important}.lc-table__checkbox{width:16px;height:16px;cursor:pointer;accent-color:var(--color-primary)}.lc-table__row--selected{background-color:var(--color-surface-selected)!important}.lc-table__actions-cell{white-space:nowrap}.lc-table th.lc-table__actions-cell--start,.lc-table td.lc-table__actions-cell--start{text-align:left}.lc-table th.lc-table__actions-cell--center,.lc-table td.lc-table__actions-cell--center{text-align:center}.lc-table th.lc-table__actions-cell--end,.lc-table td.lc-table__actions-cell--end{text-align:right}.lc-table__actions{display:flex;align-items:center;gap:var(--spacing-2);justify-content:flex-start}.lc-table__actions-cell--center .lc-table__actions{justify-content:center}.lc-table__actions-cell--end .lc-table__actions{justify-content:flex-end}.lc-table__tree-cell{display:flex;align-items:center;min-width:0}.lc-table__tree-indent{flex:0 0 auto;align-self:stretch;position:relative}.lc-table__tree-indent:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background-color:var(--color-border)}.lc-table__tree-chevron{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center}.lc-table__chevron-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--border-radius-sm);transition:transform .2s ease,background-color .15s ease,color .15s ease}.lc-table__chevron-btn:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table__chevron-btn:focus-visible{outline:2px solid var(--color-primary);outline-offset:1px}.lc-table__chevron-btn--expanded{transform:rotate(90deg)}.lc-table__tree-content{flex:1 1 auto;min-width:0}.lc-table__row--group .lc-table__tree-content{font-weight:var(--typography-font-weight-semibold)}.lc-table__edit-input{width:100%;padding:var(--spacing-1) var(--spacing-2);border:2px solid var(--color-primary);border-radius:var(--border-radius-sm);font-size:inherit;background-color:var(--color-surface-2);color:var(--color-text-primary);outline:none}.lc-table-pagination{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-1);font-size:var(--typography-font-size-sm);color:var(--color-text-secondary)}.lc-table-pagination__info{white-space:nowrap}.lc-table-pagination__controls{display:flex;align-items:center;gap:var(--spacing-2)}.lc-table-pagination__size{display:block;min-width:110px}.lc-table-pagination__btn{appearance:none;border:1px solid var(--color-border);background-color:var(--color-surface);color:var(--color-text-primary);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sm);font-size:var(--typography-font-size-base);cursor:pointer;transition:background-color .15s ease}.lc-table-pagination__btn:hover:not(:disabled){background-color:var(--color-surface-hover)}.lc-table-pagination__btn:disabled{opacity:.4;cursor:not-allowed}.lc-table-pagination__page{white-space:nowrap;min-width:60px;text-align:center}@media(max-width:640px){.lc-table-wrapper--stack .lc-table thead{display:none}.lc-table-wrapper--stack .lc-table tbody tr{display:block;margin-bottom:var(--spacing-4);border:1px solid var(--color-border);border-radius:var(--border-radius-md)}.lc-table-wrapper--stack .lc-table tbody tr td{display:block;text-align:right;padding:var(--spacing-2) var(--spacing-4);border-bottom:1px solid var(--color-divider)}.lc-table-wrapper--stack .lc-table tbody tr td:last-child{border-bottom:none}.lc-table-wrapper--stack .lc-table tbody tr td:before{content:attr(data-label);float:left;font-weight:var(--typography-font-weight-semibold);color:var(--color-text-secondary)}}\n"]
1151
+ template: "<div [class]=\"wrapperClasses()\">\n <table [class]=\"tableClasses()\" [attr.role]=\"treeMode() ? 'treegrid' : 'table'\">\n @if (caption()) {\n <!-- First child of the table, so it names the table (and the treegrid). -->\n <caption class=\"lc-table__caption\" [class.lc-table__caption--hidden]=\"!showCaption()\">{{ caption() }}</caption>\n }\n <thead>\n <!-- Filter row -->\n @if (filterable()) {\n <tr class=\"lc-table__filter-row\">\n @if (selectable()) {\n <th class=\"lc-table__select-cell\"></th>\n }\n @for (column of columns(); track column.key) {\n <th>\n @if (column.filterable !== false) {\n <lc-input\n class=\"lc-table__filter-input\"\n size=\"xs\"\n [placeholder]=\"filterPlaceholderLabel() + ' ' + column.label\"\n [ariaLabel]=\"filterAriaLabel() + ' ' + column.label\"\n [ngModel]=\"getFilterValue(column.key)\"\n (ngModelChange)=\"onFilterChange(column.key, $event)\"\n />\n }\n </th>\n }\n @if (hasActions()) {\n <th class=\"lc-table__actions-cell\"></th>\n }\n </tr>\n }\n <!-- Header row -->\n <tr>\n @if (selectable()) {\n <th class=\"lc-table__select-cell\" scope=\"col\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"allSelected()\"\n [indeterminate]=\"someSelected()\"\n (change)=\"toggleSelectAll()\"\n [attr.aria-label]=\"selectAllAriaLabel()\"\n />\n </th>\n }\n @for (column of columns(); track column.key) {\n <!-- A sortable header is a keyboard stop too: Enter/Space sort like a click. -->\n <th\n scope=\"col\"\n [class]=\"getHeaderClasses(column) + (column.cssClass ? ' ' + column.cssClass : '')\"\n [attr.aria-sort]=\"getAriaSort(column.key)\"\n [attr.tabindex]=\"column.sortable ? 0 : null\"\n [style.width]=\"column.width\"\n [title]=\"column.tooltip || ''\"\n (click)=\"handleSort(column.key)\"\n (keydown)=\"column.sortable ? onHeaderKeydown($event, column.key) : null\">\n {{ column.label }}\n @if (column.sortable && getSortState(column.key)) {\n <span class=\"sort-indicator\" [attr.aria-label]=\"getSortState(column.key) === 'asc' ? sortedAscendingLabel() : sortedDescendingLabel()\">\n @if (getSortState(column.key) === 'asc') {\n ↑\n } @else {\n ↓\n }\n </span>\n }\n </th>\n }\n @if (hasActions()) {\n <th\n scope=\"col\"\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [style.width]=\"actionsWidth()\"\n >\n {{ actionsLabel() }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (displayRows().length === 0) {\n <tr>\n <td [attr.colspan]=\"columns().length + (selectable() ? 1 : 0) + (hasActions() ? 1 : 0)\" class=\"lc-table__empty\">\n {{ emptyText() }}\n </td>\n </tr>\n } @else {\n @for (dr of displayRows(); track dr.key) {\n <!-- In tree mode the rows form the treegrid's keyboard model: a roving\n tab stop plus Arrow/Home/End/Enter/Space handling (see the\n component). Flat mode keeps plain table rows. -->\n <tr\n #dataRow\n [class.lc-table__row--selected]=\"selectable() && isRowSelected(dr.row)\"\n [class.lc-table__row--group]=\"dr.hasChildren\"\n [attr.role]=\"treeMode() ? 'row' : null\"\n [attr.tabindex]=\"rowTabindex(dr)\"\n [attr.aria-level]=\"treeMode() ? dr.level : null\"\n [attr.aria-expanded]=\"treeMode() && dr.hasChildren ? dr.expanded : null\"\n [attr.aria-posinset]=\"treeMode() ? dr.posInSet : null\"\n [attr.aria-setsize]=\"treeMode() ? dr.setSize : null\"\n [attr.aria-selected]=\"treeMode() && selectable() ? isRowSelected(dr.row) : null\"\n (click)=\"onRowClick(dr.row, $event)\"\n (focus)=\"onRowFocus(dr)\"\n (keydown)=\"onRowKeydown($event, dr)\"\n >\n @if (selectable()) {\n <td class=\"lc-table__select-cell\" [attr.role]=\"treeMode() ? 'gridcell' : null\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"isRowSelected(dr.row)\"\n (change)=\"toggleRowSelect(dr.row)\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"selectRowAriaLabel() + ' ' + (dr.absIndex + 1)\"\n />\n </td>\n }\n @for (column of columns(); track column.key) {\n <td\n [class]=\"getCellClasses(dr.row, column, dr.absIndex)\"\n [ngStyle]=\"getCellStyles(dr.row, column, dr.absIndex)\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (dblclick)=\"editable() ? startEdit(dr.row, column.key, getCellValue(dr.row, column.key)) : null\"\n >\n @if (isTreeColumn(column.key)) {\n <span class=\"lc-table__tree-cell\">\n @for (lvl of indentLevels(dr.depth); track lvl) {\n <span class=\"lc-table__tree-indent\" [style.width.px]=\"indentSize()\"></span>\n }\n <span class=\"lc-table__tree-chevron\" [style.width.px]=\"indentSize()\">\n @if (dr.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-table__chevron-btn\"\n [class.lc-table__chevron-btn--expanded]=\"dr.expanded\"\n [attr.aria-label]=\"(dr.expanded ? collapseRowAriaLabel() : expandRowAriaLabel()) + ' ' + treeCellLabel(dr.row)\"\n [attr.aria-expanded]=\"dr.expanded\"\n (click)=\"onChevronClick(dr, $event)\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n <span class=\"lc-table__tree-content\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n </span>\n </span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n }\n </td>\n }\n @if (hasActions()) {\n <td\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"lc-table__actions\">\n @for (action of actions(); track action.key) {\n @if (!isActionHidden(action, dr.row, dr.absIndex)) {\n <lc-button\n [variant]=\"action.variant ?? 'ghost'\"\n size=\"xs\"\n [iconOnly]=\"!action.label\"\n [disabled]=\"isActionDisabled(action, dr.row, dr.absIndex)\"\n [ariaLabel]=\"action.tooltip ?? action.label ?? action.key\"\n (clicked)=\"onActionClick(action, dr.row, dr.absIndex)\"\n >\n @if (action.icon) {\n <lc-icon [name]=\"action.icon\" size=\"sm\" [decorative]=\"true\" />\n }\n @if (action.label) {\n {{ action.label }}\n }\n </lc-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n<!-- Shared cell content (edit input / custom template / formatted value) -->\n<ng-template #cellContent let-row=\"row\" let-column=\"column\" let-absIndex=\"absIndex\">\n @if (isEditing(row, column.key)) {\n <input\n #editInput\n type=\"text\"\n class=\"lc-table__edit-input\"\n [value]=\"editValue()\"\n (input)=\"editValue.set(getInputValue($event))\"\n (blur)=\"commitEdit(row, column.key)\"\n (keydown)=\"onEditKeydown($event, row, column.key)\"\n />\n } @else if (hasCustomTemplate(column.key)) {\n <ng-container *ngTemplateOutlet=\"getCellTemplate(column.key)!.template; context: { $implicit: row }\"></ng-container>\n } @else {\n {{ getFormattedCellValue(row, column, absIndex) }}\n }\n</ng-template>\n\n<!-- Pagination -->\n@if (paginate()) {\n <div class=\"lc-table-pagination\">\n <div class=\"lc-table-pagination__info\">\n {{ paginationStart }}–{{ paginationEnd }} {{ rangeOfLabel() }} {{ totalRows() }}\n </div>\n <div class=\"lc-table-pagination__controls\">\n <lc-select\n class=\"lc-table-pagination__size\"\n [size]=\"size() === 'sm' ? 'sm' : size() === 'lg' ? 'lg' : 'md'\"\n [options]=\"pageSizeSelectOptions()\"\n [ngModel]=\"internalPageSize()\"\n [ariaLabel]=\"rowsPerPageAriaLabel()\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() === 0\"\n (click)=\"goToPage(currentPage() - 1)\"\n [attr.aria-label]=\"previousPageAriaLabel()\"\n >‹</button>\n <span class=\"lc-table-pagination__page\">\n {{ currentPage() + 1 }} / {{ totalPages() }}\n </span>\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() >= totalPages() - 1\"\n (click)=\"goToPage(currentPage() + 1)\"\n [attr.aria-label]=\"nextPageAriaLabel()\"\n >›</button>\n </div>\n </div>\n}\n",
1152
+ styles: [".lc-table-wrapper{width:100%}.lc-table-wrapper--responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:768px){.lc-table-wrapper--responsive::-webkit-scrollbar{height:8px}.lc-table-wrapper--responsive::-webkit-scrollbar-thumb{background-color:var(--color-border);border-radius:var(--border-radius-sm)}}.lc-table{width:100%;border-collapse:collapse;border-spacing:0;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--typography-font-family)}.lc-table thead{background-color:var(--color-surface-2);border-bottom:2px solid var(--color-border-strong)}.lc-table thead tr th{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));text-align:left;font-weight:var(--typography-font-weight-semibold);font-size:var(--typography-font-size-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-secondary);white-space:nowrap;-webkit-user-select:none;user-select:none}.lc-table thead tr th.sortable{cursor:pointer;position:relative;padding-right:var(--spacing-8);transition:background-color .2s ease}.lc-table thead tr th.sortable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-table thead tr th.sortable:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table thead tr th.sortable .sort-indicator{position:absolute;right:var(--spacing-3);top:50%;transform:translateY(-50%);font-size:var(--typography-font-size-xs);color:var(--color-primary)}.lc-table thead tr th.sorted-asc,.lc-table thead tr th.sorted-desc{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table tbody tr{border-bottom:1px solid var(--color-divider)}.lc-table tbody tr:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-table tbody tr:last-child{border-bottom:none}.lc-table tbody tr td{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));font-size:var(--typography-font-size-sm);color:var(--color-text-primary)}.lc-table__empty{text-align:center;padding:var(--spacing-8) var(--spacing-4)!important;color:var(--color-text-secondary);font-style:italic}.lc-table--striped tbody tr:nth-child(2n){background-color:var(--color-surface-2)}.lc-table--bordered{border:1px solid var(--color-border)}.lc-table--bordered thead tr th,.lc-table--bordered tbody tr td{border-right:1px solid var(--color-divider)}.lc-table--bordered thead tr th:last-child,.lc-table--bordered tbody tr td:last-child{border-right:none}.lc-table--bordered tbody tr{border-bottom:1px solid var(--color-border)}.lc-table--hoverable tbody tr,.lc-table--clickable tbody tr{transition:background-color .15s ease;cursor:pointer}.lc-table--hoverable tbody tr:hover,.lc-table--clickable tbody tr:hover{background-color:var(--color-surface-hover)}.lc-table--sm thead tr th,.lc-table--sm tbody tr td{padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--typography-font-size-xs)}.lc-table--lg thead tr th,.lc-table--lg tbody tr td{padding:var(--lc-density-padding-md, var(--spacing-4)) var(--spacing-5);font-size:var(--typography-font-size-base)}.lc-table__caption{caption-side:top;padding:0 0 var(--spacing-3);text-align:start;font-family:var(--typography-font-family);font-size:var(--typography-font-size-base);font-weight:var(--typography-font-weight-semibold);line-height:var(--typography-line-height-normal);color:var(--color-text-primary)}.lc-table__caption--hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(forced-colors:active){.lc-table tbody tr:focus-visible{outline:2px solid Highlight}}.lc-table__filter-row th{padding:var(--spacing-2) var(--spacing-4)!important;background-color:var(--color-surface)!important;border-bottom:1px solid var(--color-divider)!important}.lc-table__filter-input{width:100%;display:block}.lc-table__filter-input .input-field{font-size:var(--typography-font-size-xs)}.lc-table__select-cell{width:40px;text-align:center!important;padding:var(--spacing-2)!important}.lc-table__checkbox{width:16px;height:16px;cursor:pointer;accent-color:var(--color-primary)}.lc-table__row--selected{background-color:var(--color-surface-selected)!important}.lc-table__actions-cell{white-space:nowrap}.lc-table th.lc-table__actions-cell--start,.lc-table td.lc-table__actions-cell--start{text-align:left}.lc-table th.lc-table__actions-cell--center,.lc-table td.lc-table__actions-cell--center{text-align:center}.lc-table th.lc-table__actions-cell--end,.lc-table td.lc-table__actions-cell--end{text-align:right}.lc-table__actions{display:flex;align-items:center;gap:var(--spacing-2);justify-content:flex-start}.lc-table__actions-cell--center .lc-table__actions{justify-content:center}.lc-table__actions-cell--end .lc-table__actions{justify-content:flex-end}.lc-table__tree-cell{display:flex;align-items:center;min-width:0}.lc-table__tree-indent{flex:0 0 auto;align-self:stretch;position:relative}.lc-table__tree-indent:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background-color:var(--color-border)}.lc-table__tree-chevron{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center}.lc-table__chevron-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--border-radius-sm);transition:transform .2s ease,background-color .15s ease,color .15s ease}.lc-table__chevron-btn:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table__chevron-btn:focus-visible{outline:2px solid var(--color-primary);outline-offset:1px}.lc-table__chevron-btn--expanded{transform:rotate(90deg)}.lc-table__tree-content{flex:1 1 auto;min-width:0}.lc-table__row--group .lc-table__tree-content{font-weight:var(--typography-font-weight-semibold)}.lc-table__edit-input{width:100%;padding:var(--spacing-1) var(--spacing-2);border:2px solid var(--color-primary);border-radius:var(--border-radius-sm);font-size:inherit;background-color:var(--color-surface-2);color:var(--color-text-primary);outline:none}.lc-table-pagination{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-1);font-size:var(--typography-font-size-sm);color:var(--color-text-secondary)}.lc-table-pagination__info{white-space:nowrap}.lc-table-pagination__controls{display:flex;align-items:center;gap:var(--spacing-2)}.lc-table-pagination__size{display:block;min-width:110px}.lc-table-pagination__btn{appearance:none;border:1px solid var(--color-border);background-color:var(--color-surface);color:var(--color-text-primary);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sm);font-size:var(--typography-font-size-base);cursor:pointer;transition:background-color .15s ease}.lc-table-pagination__btn:hover:not(:disabled){background-color:var(--color-surface-hover)}.lc-table-pagination__btn:disabled{opacity:.4;cursor:not-allowed}.lc-table-pagination__page{white-space:nowrap;min-width:60px;text-align:center}@media(max-width:640px){.lc-table-wrapper--stack .lc-table thead{display:none}.lc-table-wrapper--stack .lc-table tbody tr{display:block;margin-bottom:var(--spacing-4);border:1px solid var(--color-border);border-radius:var(--border-radius-md)}.lc-table-wrapper--stack .lc-table tbody tr td{display:block;text-align:right;padding:var(--spacing-2) var(--spacing-4);border-bottom:1px solid var(--color-divider)}.lc-table-wrapper--stack .lc-table tbody tr td:last-child{border-bottom:none}.lc-table-wrapper--stack .lc-table tbody tr td:before{content:attr(data-label);float:left;font-weight:var(--typography-font-weight-semibold);color:var(--color-text-secondary)}}\n"]
1113
1153
  }]
1114
1154
  }],
1115
1155
  ctorParameters: () => [],
@@ -1158,6 +1198,30 @@ i0.ɵɵngDeclareClassMetadata({
1158
1198
  required: false
1159
1199
  }]
1160
1200
  }],
1201
+ clickable: [{
1202
+ type: i0.Input,
1203
+ args: [{
1204
+ isSignal: true,
1205
+ alias: "clickable",
1206
+ required: false
1207
+ }]
1208
+ }],
1209
+ caption: [{
1210
+ type: i0.Input,
1211
+ args: [{
1212
+ isSignal: true,
1213
+ alias: "caption",
1214
+ required: false
1215
+ }]
1216
+ }],
1217
+ showCaption: [{
1218
+ type: i0.Input,
1219
+ args: [{
1220
+ isSignal: true,
1221
+ alias: "showCaption",
1222
+ required: false
1223
+ }]
1224
+ }],
1161
1225
  responsive: [{
1162
1226
  type: i0.Input,
1163
1227
  args: [{