codexly-ui 0.13.5 → 0.14.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.
@@ -2788,6 +2788,15 @@ class ClxAnimateService {
2788
2788
  void element.offsetHeight;
2789
2789
  const animClass = `${PREFIX}${config.animation}`;
2790
2790
  const isInfinite = config.repeat === 'infinite';
2791
+ // A fillMode: 'forwards' animation (an exit fade/slide, e.g. clx-toast's playExitAnimation)
2792
+ // is almost always followed by the caller destroying the element on the same tick its promise
2793
+ // resolves — stripping animClass in _cleanup right then would drop animation-fill-mode's hold
2794
+ // on the last keyframe, snapping the element back to its static CSS state (opaque, in place)
2795
+ // for one paintable frame before removal, which read as a flash/"ghost" re-appearance. Classes
2796
+ // are only stripped for a non-forwards run; a forwards run keeps them and instead resets
2797
+ // them at the START of runAnimation's own reflow-forced restart path above, so a persisted
2798
+ // element that's re-animated later (e.g. clx-animate-directive on hover) still resets cleanly.
2799
+ const keepClassesOnEnd = config.fillMode === 'forwards';
2791
2800
  element.style.setProperty('--clx-anim-duration', `${config.duration}ms`);
2792
2801
  element.style.setProperty('--clx-anim-delay', `${config.delay}ms`);
2793
2802
  element.style.setProperty('--clx-anim-repeat', isInfinite ? 'infinite' : String(config.repeat ?? 1));
@@ -2809,7 +2818,12 @@ class ClxAnimateService {
2809
2818
  return new Promise(resolve => {
2810
2819
  const handleEnd = (ev) => {
2811
2820
  ev.stopPropagation();
2812
- this._cleanup(element, animClass);
2821
+ if (keepClassesOnEnd) {
2822
+ this._cleanups.delete(element);
2823
+ }
2824
+ else {
2825
+ this._cleanup(element, animClass);
2826
+ }
2813
2827
  this._runningCount.update(c => Math.max(0, c - 1));
2814
2828
  resolve();
2815
2829
  };
@@ -14990,6 +15004,158 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14990
15004
  }]
14991
15005
  }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "shadow", required: false }] }], triggerLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerLabel", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }] } });
14992
15006
 
15007
+ /** Generic sample values keyed by variable name — covers the real {{variable}} vocabulary already
15008
+ * used across Codexly's notification/label templates (see NotificationTypeCatalog on the backend
15009
+ * and LABEL_TEMPLATE_VARS on the frontend), so a preview looks realistic with zero configuration.
15010
+ * A consumer can still override/extend via the `vars` input; this is only the fallback for any
15011
+ * key it doesn't supply. */
15012
+ const TEMPLATE_PREVIEW_SAMPLE_VARS = {
15013
+ orderNumber: 'ORD-4471',
15014
+ orderId: 'ORD-4471',
15015
+ amount: '198.000',
15016
+ toStatus: 'Confirmada',
15017
+ fromStatus: 'Pendiente',
15018
+ trackingNumber: 'TRK-88213045',
15019
+ customerName: 'Juan Pérez',
15020
+ customerPhone: '+57 300 123 4567',
15021
+ customerAddress: 'Carrera 45 # 12-34',
15022
+ customerCity: 'Bogotá',
15023
+ customerNeighborhood: 'Chapinero',
15024
+ companyName: 'CodexlyShop',
15025
+ companyStyledName: 'Codexly<span style="color:#f5c400">Shop</span>',
15026
+ companyLogo: '',
15027
+ senderLabel: 'Transportadora',
15028
+ senderName: 'Envía',
15029
+ variantName: 'Casco abatible ICH 3120',
15030
+ variantSku: 'CAS-3120-NEG',
15031
+ sellerName: 'David Medina',
15032
+ referenceId: 'a1b2c3d4-0000-0000-0000-000000000000',
15033
+ };
15034
+ /** Naive {{key}} → value string replacement — the exact primitive already used by
15035
+ * ShipmentLabelModalComponent (`html.split('{{key}}').join(v)`) and mirrored server-side by
15036
+ * NotificationDispatcher.render() (Java, plain .replace loop, no expressions). Kept here as the
15037
+ * single shared implementation so every consumer (this component, ShipmentLabelModalComponent)
15038
+ * renders previews identically instead of drifting. Unresolved {{key}} placeholders are left
15039
+ * as-is (visible literally), same as the real dispatchers when a var wasn't supplied. */
15040
+ function renderClxTemplate(body, vars) {
15041
+ let result = body;
15042
+ for (const [key, value] of Object.entries(vars)) {
15043
+ result = result.split(`{{${key}}}`).join(value);
15044
+ }
15045
+ return result;
15046
+ }
15047
+
15048
+ /** Read-only live preview of a `{{variable}}` template — for a template EDITOR (notification
15049
+ * templates, shipping label templates, ...) to show what the body will actually look like once
15050
+ * substituted, instead of only exposing the raw markup/placeholders. "Dumb" like ClxNotification/
15051
+ * ClxToast: it never fetches sample data itself — pass real values via `vars`, or rely on the
15052
+ * built-in generic fallbacks (TEMPLATE_PREVIEW_SAMPLE_VARS) for any key you don't supply.
15053
+ *
15054
+ * Substitution is the same naive {{key}} → value replace used by the real dispatchers (backend
15055
+ * NotificationDispatcher.render() in Java, and ShipmentLabelModalComponent client-side) — no
15056
+ * expression evaluation, so a Thymeleaf-flavored body (th:if/th:each, used only by the PROMOTION
15057
+ * notification type) will show its raw tags rather than truly rendering; that's a known, accepted
15058
+ * gap, not a bug — this component previews the plain {{var}} vocabulary every other template uses. */
15059
+ class ClxTemplatePreviewComponent {
15060
+ _sanitizer = inject(DomSanitizer);
15061
+ /** Raw template body — plain text (toast/in-app) or HTML (rich, e.g. an email body). */
15062
+ body = input('', ...(ngDevMode ? [{ debugName: "body" }] : /* istanbul ignore next */ []));
15063
+ /** Optional separate title/subject, substituted the same way as body. Shown above the message in
15064
+ * plain-toast/plain-inapp mode; ignored in rich mode (the body itself carries its own heading). */
15065
+ title = input(undefined, ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
15066
+ mode = input('rich', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
15067
+ /** Material Symbols icon shown in plain-toast/plain-inapp mode — defaults to 'notifications'. */
15068
+ icon = input(undefined, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
15069
+ /** Sample values for {{key}} substitution — merged OVER the built-in generic fallbacks
15070
+ * (TEMPLATE_PREVIEW_SAMPLE_VARS), so you only need to pass the keys you want to control
15071
+ * (e.g. a real shipment's data) and every other {{var}} still resolves to something readable. */
15072
+ vars = input(undefined, ...(ngDevMode ? [{ debugName: "vars" }] : /* istanbul ignore next */ []));
15073
+ _mergedVars = computed(() => ({
15074
+ ...TEMPLATE_PREVIEW_SAMPLE_VARS,
15075
+ ...(this.vars() ?? {}),
15076
+ }), ...(ngDevMode ? [{ debugName: "_mergedVars" }] : /* istanbul ignore next */ []));
15077
+ _title = computed(() => this.title() ? renderClxTemplate(this.title(), this._mergedVars()) : '', ...(ngDevMode ? [{ debugName: "_title" }] : /* istanbul ignore next */ []));
15078
+ _renderedText = computed(() => renderClxTemplate(this.body(), this._mergedVars()), ...(ngDevMode ? [{ debugName: "_renderedText" }] : /* istanbul ignore next */ []));
15079
+ _renderedHtml = computed(() => this._sanitizer.bypassSecurityTrustHtml(renderClxTemplate(this.body(), this._mergedVars())), ...(ngDevMode ? [{ debugName: "_renderedHtml" }] : /* istanbul ignore next */ []));
15080
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTemplatePreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15081
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxTemplatePreviewComponent, isStandalone: true, selector: "clx-template-preview", inputs: { body: { classPropertyName: "body", publicName: "body", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, vars: { classPropertyName: "vars", publicName: "vars", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
15082
+ @switch (mode()) {
15083
+ @case ('plain-toast') {
15084
+ <div class="flex items-start gap-3.5 w-full rounded-xl border border-clx-border-soft bg-clx-surface px-4 py-3.5">
15085
+ <span class="shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-clx-primary">
15086
+ <span clx-icon [name]="icon() ?? 'notifications'" size="sm" class="text-white"></span>
15087
+ </span>
15088
+ <div class="flex-1 min-w-0 pt-0.5">
15089
+ @if (_title()) {
15090
+ <p class="text-sm font-semibold leading-snug mb-0.5 text-clx-text-label">{{ _title() }}</p>
15091
+ }
15092
+ <p class="text-sm text-clx-text leading-snug wrap-break-word opacity-80">{{ _renderedText() }}</p>
15093
+ </div>
15094
+ </div>
15095
+ }
15096
+ @case ('plain-inapp') {
15097
+ <div class="flex items-start gap-3 rounded-lg border border-clx-border-soft bg-clx-surface px-3 py-2.5">
15098
+ <span class="flex items-center justify-center rounded-lg shrink-0 w-8.5 h-8.5 mt-0.5 bg-clx-primary-subtle text-clx-primary">
15099
+ <span clx-icon [name]="icon() ?? 'notifications'" size="sm"></span>
15100
+ </span>
15101
+ <div class="flex-1 min-w-0">
15102
+ @if (_title()) {
15103
+ <p class="text-sm font-[600] text-clx-text-label">{{ _title() }}</p>
15104
+ }
15105
+ <p class="mt-0.5 text-sm text-clx-text-muted">{{ _renderedText() }}</p>
15106
+ </div>
15107
+ </div>
15108
+ }
15109
+ @default {
15110
+ <div class="rounded-xl border border-clx-border-soft bg-white overflow-hidden" [innerHTML]="_renderedHtml()"></div>
15111
+ }
15112
+ }
15113
+ `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
15114
+ }
15115
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTemplatePreviewComponent, decorators: [{
15116
+ type: Component,
15117
+ args: [{
15118
+ selector: 'clx-template-preview',
15119
+ standalone: true,
15120
+ imports: [ClxIconComponent],
15121
+ encapsulation: ViewEncapsulation.None,
15122
+ changeDetection: ChangeDetectionStrategy.OnPush,
15123
+ template: `
15124
+ @switch (mode()) {
15125
+ @case ('plain-toast') {
15126
+ <div class="flex items-start gap-3.5 w-full rounded-xl border border-clx-border-soft bg-clx-surface px-4 py-3.5">
15127
+ <span class="shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-clx-primary">
15128
+ <span clx-icon [name]="icon() ?? 'notifications'" size="sm" class="text-white"></span>
15129
+ </span>
15130
+ <div class="flex-1 min-w-0 pt-0.5">
15131
+ @if (_title()) {
15132
+ <p class="text-sm font-semibold leading-snug mb-0.5 text-clx-text-label">{{ _title() }}</p>
15133
+ }
15134
+ <p class="text-sm text-clx-text leading-snug wrap-break-word opacity-80">{{ _renderedText() }}</p>
15135
+ </div>
15136
+ </div>
15137
+ }
15138
+ @case ('plain-inapp') {
15139
+ <div class="flex items-start gap-3 rounded-lg border border-clx-border-soft bg-clx-surface px-3 py-2.5">
15140
+ <span class="flex items-center justify-center rounded-lg shrink-0 w-8.5 h-8.5 mt-0.5 bg-clx-primary-subtle text-clx-primary">
15141
+ <span clx-icon [name]="icon() ?? 'notifications'" size="sm"></span>
15142
+ </span>
15143
+ <div class="flex-1 min-w-0">
15144
+ @if (_title()) {
15145
+ <p class="text-sm font-[600] text-clx-text-label">{{ _title() }}</p>
15146
+ }
15147
+ <p class="mt-0.5 text-sm text-clx-text-muted">{{ _renderedText() }}</p>
15148
+ </div>
15149
+ </div>
15150
+ }
15151
+ @default {
15152
+ <div class="rounded-xl border border-clx-border-soft bg-white overflow-hidden" [innerHTML]="_renderedHtml()"></div>
15153
+ }
15154
+ }
15155
+ `,
15156
+ }]
15157
+ }], propDecorators: { body: [{ type: i0.Input, args: [{ isSignal: true, alias: "body", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], vars: [{ type: i0.Input, args: [{ isSignal: true, alias: "vars", required: false }] }] } });
15158
+
14993
15159
  // ── Base classes ─────────────────────────────────────────────────────────────
14994
15160
  const TABLE_BASE_CLASS = 'w-full text-left border-collapse text-sm';
14995
15161
  const TABLE_HEADER_CELL_CLASS = 'px-4 py-3 font-semibold whitespace-nowrap';
@@ -18705,5 +18871,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
18705
18871
  * Generated bundle index. Do not edit.
18706
18872
  */
18707
18873
 
18708
- export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardExpiryInputComponent, ClxCardFooterDirective, ClxCardHeaderActionsDirective, ClxCardHeaderDirective, ClxCardNumberInputComponent, ClxCardValidators, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxCheckboxComponent, ClxCollapseComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorImageModalComponent, ClxEditorLinkModalComponent, ClxFabComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxMenuItemTrailingDirective, ClxModalComponent, ClxModalService, ClxNativeOverlayService, ClxNavGroupComponent, ClxNotificationComponent, ClxNumberComponent, ClxOtpComponent, ClxPageEmptyComponent, ClxPageHeaderComponent, ClxPageHeaderTitleDirective, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProductQuickViewComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSearchComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSocialIconComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableActionsComponent, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, detectClxCardBrand, isClxCardExpired, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
18874
+ export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardExpiryInputComponent, ClxCardFooterDirective, ClxCardHeaderActionsDirective, ClxCardHeaderDirective, ClxCardNumberInputComponent, ClxCardValidators, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxCheckboxComponent, ClxCollapseComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorImageModalComponent, ClxEditorLinkModalComponent, ClxFabComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxMenuItemTrailingDirective, ClxModalComponent, ClxModalService, ClxNativeOverlayService, ClxNavGroupComponent, ClxNotificationComponent, ClxNumberComponent, ClxOtpComponent, ClxPageEmptyComponent, ClxPageHeaderComponent, ClxPageHeaderTitleDirective, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProductQuickViewComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSearchComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSocialIconComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableActionsComponent, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTemplatePreviewComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TEMPLATE_PREVIEW_SAMPLE_VARS, TIMEPICKER_SIZE_MAP, detectClxCardBrand, isClxCardExpired, parseColorInput, provideCodexlyTheme, renderClxTemplate, resolveColor, resolveContainerRadius, resolveRadius };
18709
18875
  //# sourceMappingURL=codexly-ui.mjs.map