@sdcorejs/angular 21.1.4 → 21.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,11 @@
1
- import { InjectionToken, signal, inject, DestroyRef, effect, untracked, computed, booleanAttribute } from '@angular/core';
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, signal, inject, DestroyRef, effect, untracked, computed, booleanAttribute, Injectable } from '@angular/core';
2
3
  import { FormControl, NgForm, FormGroup, Validators } from '@angular/forms';
3
4
  import { Subject } from 'rxjs';
4
5
  import { startWith } from 'rxjs/operators';
6
+ import { DateFnsAdapter, provideDateFnsAdapter } from '@angular/material-date-fns-adapter';
7
+ import { DateAdapter } from '@angular/material/core';
8
+ import { parse, isValid } from 'date-fns';
5
9
 
6
10
  const SD_FORM_CONFIGURATION = new InjectionToken('sd.form.configuration');
7
11
 
@@ -312,9 +316,57 @@ function ɵsdFormControlConnector(options) {
312
316
  };
313
317
  }
314
318
 
319
+ /**
320
+ * DateAdapter từ chối mọi chuỗi người dùng chưa gõ xong.
321
+ *
322
+ * WHY cần đến mức này: `<input matInput [matDatepicker]>` khiến Material tự
323
+ * `parse()` lại ô nhập SAU MỖI PHÍM GÕ rồi ghi thẳng kết quả vào form control.
324
+ * Adapter mặc định quá dễ dãi trên hai đường:
325
+ *
326
+ * 1. `date-fns.parse` nhận năm thiếu chữ số → `11/12/2` thành năm 0002,
327
+ * `11/12/20` thành năm 0020. Ô nhập vừa gõ dở đã bị coi là hợp lệ (cờ lỗi
328
+ * bị xoá) và control ôm một ngày rác.
329
+ * 2. `parseISO` coi chuỗi 2 chữ số là thế kỷ → xoá lùi còn `11` ra năm 1100.
330
+ *
331
+ * Cả hai đường đều bị chặn ở đây: control chỉ nhận giá trị khi người dùng thực
332
+ * sự gõ xong.
333
+ */
334
+ class SdStrictDateFnsAdapter extends DateFnsAdapter {
335
+ parse(value, parseFormat) {
336
+ if (typeof value !== 'string')
337
+ return super.parse(value, parseFormat);
338
+ const trimmed = value.trim();
339
+ if (!trimmed)
340
+ return null;
341
+ // Cố tình KHÔNG gọi super.parse: chỗ đó có ISO fallback (đường số 2 ở trên).
342
+ for (const currentFormat of Array.isArray(parseFormat) ? parseFormat : [parseFormat]) {
343
+ const parsed = parse(trimmed, currentFormat, new Date(), { locale: this.locale });
344
+ if (!isValid(parsed))
345
+ continue;
346
+ // Round-trip: chuỗi phải khớp 1-1 với format, nên năm thiếu chữ số bị loại.
347
+ if (this.format(parsed, currentFormat) === trimmed)
348
+ return parsed;
349
+ }
350
+ return null;
351
+ }
352
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdStrictDateFnsAdapter, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
353
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdStrictDateFnsAdapter });
354
+ }
355
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdStrictDateFnsAdapter, decorators: [{
356
+ type: Injectable
357
+ }] });
358
+ /** `provideDateFnsAdapter` + ép dùng adapter parse chặt ở trên. */
359
+ function provideSdStrictDateFnsAdapter(formats) {
360
+ return [
361
+ provideDateFnsAdapter(formats),
362
+ // Phải đứng SAU provideDateFnsAdapter để ghi đè DateAdapter mà nó đăng ký.
363
+ { provide: DateAdapter, useClass: SdStrictDateFnsAdapter },
364
+ ];
365
+ }
366
+
315
367
  /**
316
368
  * Generated bundle index. Do not edit.
317
369
  */
318
370
 
319
- export { HandleSdCustomValidator, SD_FORM_CONFIGURATION, SdFormControl, SdInlineErrorValidator, sdFormControlState, sdViewedInline, sdViewedTransform, ɵsdCoerceFormGroup, ɵsdFormControlConnector };
371
+ export { HandleSdCustomValidator, SD_FORM_CONFIGURATION, SdFormControl, SdInlineErrorValidator, SdStrictDateFnsAdapter, provideSdStrictDateFnsAdapter, sdFormControlState, sdViewedInline, sdViewedTransform, ɵsdCoerceFormGroup, ɵsdFormControlConnector };
320
372
  //# sourceMappingURL=sdcorejs-angular-forms-models.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-forms-models.mjs","sources":["../../../projects/sdcorejs-angular/forms/models/src/sd-form.configuration.ts","../../../projects/sdcorejs-angular/forms/models/src/sd-form-control.model.ts","../../../projects/sdcorejs-angular/forms/models/src/sd-custom-validator.model.ts","../../../projects/sdcorejs-angular/forms/models/src/form-control-state.ts","../../../projects/sdcorejs-angular/forms/models/src/sd-viewed.ts","../../../projects/sdcorejs-angular/forms/models/src/sd-form-control-connector.ts","../../../projects/sdcorejs-angular/forms/models/sdcorejs-angular-forms-models.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport { MatFormFieldAppearance } from '@angular/material/form-field';\n\nexport interface ISdFormConfiguration {\n appearance?: MatFormFieldAppearance;\n}\n\nexport const SD_FORM_CONFIGURATION = new InjectionToken<ISdFormConfiguration>('sd.form.configuration');\n","import { FormControl, AsyncValidatorFn, ValidatorFn, FormControlOptions, FormControlState } from '@angular/forms';\nimport { Subject } from 'rxjs';\n\nexport class SdFormControl extends FormControl {\n sdChanges: Subject<boolean> = new Subject<boolean>();\n untouchChanges: Subject<boolean> = new Subject<boolean>();\n touchChanges: Subject<boolean> = new Subject<boolean>();\n pristineChanges: Subject<boolean> = new Subject<boolean>();\n constructor(\n formState?: FormControlState<any>,\n validatorOrOpts?: ValidatorFn | ValidatorFn[] | FormControlOptions | null,\n asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null\n ) {\n super(formState, validatorOrOpts, asyncValidator);\n }\n\n override markAsUntouched(opts?: { onlySelf?: boolean; emitEvent?: boolean }): void {\n super.markAsUntouched(opts);\n this.untouchChanges.next(true);\n this.sdChanges.next(true);\n }\n\n override markAsTouched(opts?: { onlySelf?: boolean; emitEvent?: boolean }): void {\n super.markAsTouched(opts);\n this.touchChanges.next(true);\n this.sdChanges.next(true);\n }\n\n override markAsPristine(opts?: { onlySelf?: boolean; emitEvent?: boolean }): void {\n super.markAsPristine(opts);\n this.pristineChanges.next(true);\n this.sdChanges.next(true);\n }\n}\n","import { AbstractControl, AsyncValidatorFn, ValidatorFn } from '@angular/forms';\n\nexport type SdCustomValidator = (value: any) => string | Promise<string>;\n\n/**\n * Inline-error sentinel validator. Returns `{ inlineError: true }` so the form\n * template can render `<mat-error>{{ inlineError }}</mat-error>` whenever the\n * host component has a non-empty `[inlineError]` input. The error message\n * itself is read from the input — this validator only flags the state.\n *\n * why: each form component (input, textarea, select, checkbox, radio, switch,\n * date, datetime, input-number, autocomplete) previously declared the same\n * private `customInlineErrorValidator()` method. Centralized here so adding\n * another form component does not silently re-introduce the duplicate.\n */\nexport const SdInlineErrorValidator: ValidatorFn = (): Record<string, unknown> | null => ({ inlineError: true });\n\nexport const HandleSdCustomValidator = (func: SdCustomValidator): AsyncValidatorFn => {\n return async (c: AbstractControl): Promise<Record<string, any> | null> => {\n const value = c.value === 0 ? c.value : c.value || null;\n if (func && typeof func === 'function') {\n const result = func(value);\n if (result instanceof Promise) {\n const message = await result;\n if (message) {\n return {\n customValidator: message,\n };\n }\n return null;\n }\n if (result) {\n return {\n customValidator: result,\n };\n }\n return null;\n }\n return null;\n };\n};\n","import { DestroyRef, EffectRef, Signal, computed, effect, inject, signal, untracked } from '@angular/core';\nimport { AbstractControl } from '@angular/forms';\nimport { Subscription } from 'rxjs';\nimport { startWith } from 'rxjs/operators';\n\nexport interface SdFormControlSnapshot<T> {\n value: T | undefined;\n disabled: boolean;\n invalid: boolean;\n touched: boolean;\n}\n\n/**\n * Wrap an AbstractControl Signal into a reactive snapshot signal.\n *\n * Re-emits on every value, status, touched, or dirty change so\n * downstream consumers can derive `data-disabled`, `data-value`,\n * `data-empty`, and `data-invalid` host-binding attributes from a\n * single, lazily evaluated signal.\n *\n * \"invalid\" is intentionally gated on `touched || dirty` so validation\n * errors are not surfaced until the user has interacted with the field.\n *\n * Must be called inside an Angular injection context (constructor,\n * field initialiser, or `runInInjectionContext`).\n */\nexport function sdFormControlState<T = unknown>(control: Signal<AbstractControl<T> | null | undefined>): Signal<SdFormControlSnapshot<T>> {\n // A tick counter incremented on every control event (value, status,\n // touched, dirty). Written only from the effect below — never inside\n // a computed() — satisfying Angular's no-side-effect-in-reactive rule.\n const tick = signal(0);\n const destroyRef = inject(DestroyRef);\n\n // Holds the active RxJS subscription so it can be torn down when the\n // control instance changes or the host component is destroyed.\n let subscription: Subscription | null = null;\n\n // An effect re-runs whenever the control Signal changes, tears down the\n // old subscription and creates a fresh one for the incoming control.\n // `AbstractControl.events` (Angular 14+) covers value, status, touched,\n // and dirty changes — a superset of valueChanges + statusChanges.\n const effectRef: EffectRef = effect(() => {\n const c = control(); // tracked — effect re-runs on change\n // effect() runs asynchronously; the old subscription is torn down on the next\n // scheduled run, not synchronously on signal change. A one-tick window of\n // double-subscription is harmless due to computed() memoization.\n subscription?.unsubscribe();\n subscription = null;\n\n if (c) {\n subscription = c.events.pipe(startWith(null)).subscribe(() => {\n // Increment outside reactive context to avoid Angular's\n // \"signal written from computed / template\" error.\n untracked(() => tick.update(n => n + 1));\n });\n }\n });\n\n // Clean up effect and subscription when host component is destroyed.\n destroyRef.onDestroy(() => {\n effectRef.destroy();\n subscription?.unsubscribe();\n });\n\n return computed((): SdFormControlSnapshot<T> => {\n const _control = control();\n tick(); // reactive dependency — recomputes on every control event\n\n if (!_control) {\n return { value: undefined, disabled: false, invalid: false, touched: false };\n }\n\n return {\n value: _control.value as T,\n disabled: _control.disabled,\n invalid: _control.invalid && (_control.touched || _control.dirty),\n touched: _control.touched,\n };\n });\n}\n","import { booleanAttribute, computed, Signal } from '@angular/core';\n\n/**\n * Three display states shared by sd-form-controls:\n * - `false` → full edit chrome (input / dropdown).\n * - `true` → static read-only view (`<sd-view>` text), no editor.\n * - `'inline'` → the editor is STILL rendered (so its panel works), but its chrome is\n * hidden; the `<sd-view>` text is the visible face / trigger. Click the text to open\n * the picker's panel. The text is retained while the panel is open — it only changes\n * when a new value is committed (JIRA-style click-to-edit).\n */\nexport type SdViewed = boolean | 'inline';\nexport type SdViewedInput = SdViewed | '' | null | undefined;\n\n/**\n * `viewed` input transform. Keeps `booleanAttribute` coercion so a bare attribute\n * (`<sd-select viewed>`) still resolves to `true`, but intercepts the literal\n * `'inline'` first — `booleanAttribute('inline')` would otherwise coerce it to `true`.\n */\nexport function sdViewedTransform(v: SdViewedInput): SdViewed {\n return v === 'inline' ? 'inline' : booleanAttribute(v);\n}\n\nexport interface SdViewedInlineApi {\n /** `viewed() === 'inline'` — editor rendered but chrome hidden; sd-view text is the trigger face. */\n readonly isInline: Signal<boolean>;\n /** `viewed() === true` — static read-only view (no editor rendered). */\n readonly isViewed: Signal<boolean>;\n /** Open the picker from the inline text face. No-op unless `'inline'`. */\n enterInlineEdit(): void;\n}\n\n/**\n * Compose the tri-state `viewed` semantics into a control. `open` opens the control's\n * native picker (mat-select panel / mat-calendar / overlay). In `'inline'` mode the editor\n * is always rendered (chrome hidden via CSS), so `open()` can fire immediately on click —\n * no render-swap, the view text never disappears.\n *\n * @param viewed the control's `viewed` input signal.\n * @param open opens the control's picker; called by `enterInlineEdit`.\n * @param disabled the control's disabled state. why: a disabled `'inline'` field must behave\n * like `viewed=true` (static, NOT click-to-edit) — you can't edit a disabled control.\n */\nexport function sdViewedInline(viewed: Signal<SdViewed>, open?: () => void, disabled?: Signal<boolean>): SdViewedInlineApi {\n // why: disabled biến 'inline' thành static view (isInline=false, isViewed=true) — không cho sửa.\n const isInline = computed(() => viewed() === 'inline' && !disabled?.());\n const isViewed = computed(() => viewed() === true || (viewed() === 'inline' && !!disabled?.()));\n const enterInlineEdit = (): void => {\n if (isInline()) open?.();\n };\n return { isInline, isViewed, enterInlineEdit };\n}\n","import { Signal, computed, effect, untracked } from '@angular/core';\nimport { AbstractControl, AsyncValidatorFn, FormGroup, NgForm, ValidatorFn, Validators } from '@angular/forms';\n\nimport { sdFormControlState } from './form-control-state';\nimport { SdViewed } from './sd-viewed';\n\nexport type ɵSdFormControlParent = FormGroup | NgForm | { readonly form: unknown } | null | undefined;\n\ninterface ɵSdFormControlConnectorBaseOptions<TControl> {\n /** Parent form source. NgForm and wrapper values are unwrapped on every rebind. */\n readonly form: Signal<ɵSdFormControlParent>;\n /** Registration name. Empty names intentionally leave the control unregistered. */\n readonly name: Signal<string | null | undefined>;\n /** Canonical control registered in the parent form. */\n readonly control: Signal<AbstractControl<TControl>>;\n readonly validators?: Signal<ValidatorFn | readonly ValidatorFn[] | null | undefined>;\n readonly asyncValidators?: Signal<AsyncValidatorFn | readonly AsyncValidatorFn[] | null | undefined>;\n /** Adds/removes Validators.required while preserving validators supplied above. */\n readonly required?: Signal<boolean | null | undefined>;\n readonly disabled?: Signal<boolean | null | undefined>;\n /** UI-only read-only policy. This never disables the Angular control. */\n readonly readonly?: Signal<boolean | null | undefined>;\n /** Exact SDCoreJS display policy. This never disables the Angular control. */\n readonly viewed?: Signal<SdViewed | null | undefined>;\n /** Component-local validation message. Visibility is interaction-gated in state. */\n readonly validationError?: Signal<string | null | undefined>;\n}\n\ninterface ɵSdFormControlRegistrationOptions<TControl> extends ɵSdFormControlConnectorBaseOptions<TControl> {\n readonly model?: never;\n readonly writeModel?: never;\n readonly modelToControl?: never;\n readonly controlToModel?: never;\n readonly modelEquals?: never;\n readonly controlEquals?: never;\n}\n\ninterface ɵSdFormControlIdentityOptions<TValue> extends ɵSdFormControlConnectorBaseOptions<TValue> {\n readonly model: Signal<TValue>;\n readonly writeModel: (value: TValue) => void;\n readonly modelToControl?: never;\n readonly controlToModel?: never;\n readonly modelEquals?: (left: TValue, right: TValue) => boolean;\n readonly controlEquals?: (left: TValue, right: TValue) => boolean;\n}\n\ninterface ɵSdFormControlAdaptedOptions<TModel, TControl> extends ɵSdFormControlConnectorBaseOptions<TControl> {\n readonly model: Signal<TModel>;\n readonly writeModel: (value: TModel) => void;\n readonly modelToControl: (value: TModel) => TControl;\n readonly controlToModel: (value: TControl) => TModel;\n readonly modelEquals?: (left: TModel, right: TModel) => boolean;\n readonly controlEquals?: (left: TControl, right: TControl) => boolean;\n}\n\ntype ɵSdTypesExactlyMatch<TLeft, TRight> = [TLeft] extends [TRight] ? ([TRight] extends [TLeft] ? true : false) : false;\n\n/**\n * @internal Unstable connector contract for cross-entrypoint SDCoreJS controls.\n * Consumers must use registration-only, same-type identity binding, or provide\n * both adapters when model and control representations differ.\n */\nexport type ɵSdFormControlConnectorOptions<TModel, TControl> =\n | ɵSdFormControlRegistrationOptions<TControl>\n | ɵSdFormControlAdaptedOptions<TModel, TControl>\n | (ɵSdTypesExactlyMatch<TModel, TControl> extends true ? ɵSdFormControlIdentityOptions<TModel> : never);\n\ntype ɵSdFormControlBindingOptions<TModel, TControl> =\n | ɵSdFormControlIdentityOptions<TControl>\n | ɵSdFormControlAdaptedOptions<TModel, TControl>;\n\ntype ɵSdFormControlImplementationOptions<TModel, TControl> =\n | ɵSdFormControlRegistrationOptions<TControl>\n | ɵSdFormControlBindingOptions<TModel, TControl>;\n\nexport interface ɵSdFormControlConnectorState<TControl> {\n readonly value: TControl | undefined;\n readonly disabled: boolean;\n readonly invalid: boolean;\n readonly touched: boolean;\n readonly dirty: boolean;\n readonly required: boolean;\n readonly readonly: boolean;\n readonly viewed: SdViewed;\n readonly isViewed: boolean;\n readonly isInline: boolean;\n readonly showValidationError: boolean;\n readonly validationError: string | undefined;\n}\n\nexport interface ɵSdFormControlConnector<TControl = unknown> {\n readonly state: Signal<ɵSdFormControlConnectorState<TControl>>;\n markAsTouched(): void;\n markAsUntouched(): void;\n markAsDirty(): void;\n markAsPristine(): void;\n}\n\n/** Coerces the form shapes accepted by SDCoreJS controls into one FormGroup. */\nexport function ɵsdCoerceFormGroup(value: unknown): FormGroup | undefined {\n if (value instanceof NgForm) return value.form;\n if (value instanceof FormGroup) return value;\n if (typeof value !== 'object' || value === null || !('form' in value)) return undefined;\n\n const wrappedForm = (value as { readonly form: unknown }).form;\n return wrappedForm instanceof FormGroup ? wrappedForm : undefined;\n}\n\nfunction normalizeValidatorList<TValidator extends ValidatorFn | AsyncValidatorFn>(\n value: TValidator | readonly TValidator[] | null | undefined\n): TValidator[] {\n if (!value) return [];\n return typeof value === 'function' ? [value] : [...value];\n}\n\nfunction hasModelBinding<TModel, TControl>(\n options: ɵSdFormControlImplementationOptions<TModel, TControl>\n): options is ɵSdFormControlBindingOptions<TModel, TControl> {\n return options.model !== undefined && options.writeModel !== undefined;\n}\n\nfunction readControlValue<TModel, TControl>(options: ɵSdFormControlBindingOptions<TModel, TControl>): TControl {\n if (options.modelToControl) return options.modelToControl(options.model());\n return options.model();\n}\n\nfunction writeModelValue<TModel, TControl>(options: ɵSdFormControlBindingOptions<TModel, TControl>, controlValue: TControl): void {\n if (options.controlToModel) {\n const modelValue = options.controlToModel(controlValue);\n if (!(options.modelEquals ?? Object.is)(options.model(), modelValue)) options.writeModel(modelValue);\n return;\n }\n\n if (!(options.modelEquals ?? Object.is)(options.model(), controlValue)) options.writeModel(controlValue);\n}\n\n/**\n * Connects the signal-based SDCoreJS model contract to an Angular control.\n * Registration and subscriptions are rebound transactionally, and cleanup only\n * removes a control while the connector still owns that exact registration.\n */\nexport function ɵsdFormControlConnector<TModel, TControl>(\n options: ɵSdFormControlConnectorOptions<TModel, TControl>\n): ɵSdFormControlConnector<TControl>;\nexport function ɵsdFormControlConnector<TModel, TControl>(\n options: ɵSdFormControlImplementationOptions<TModel, TControl>\n): ɵSdFormControlConnector<TControl> {\n const controlEquals = options.controlEquals ?? Object.is;\n const controlState = sdFormControlState(options.control);\n const state = computed((): ɵSdFormControlConnectorState<TControl> => {\n const snapshot = controlState();\n const required = !!options.required?.();\n const readonly = !!options.readonly?.();\n const viewed = options.viewed?.() ?? false;\n const validationError = options.validationError?.() || undefined;\n const showValidationError = snapshot.invalid && validationError !== undefined;\n\n return {\n ...snapshot,\n dirty: options.control().dirty,\n required,\n readonly,\n viewed,\n isViewed: viewed === true,\n isInline: viewed === 'inline',\n showValidationError,\n validationError: showValidationError ? validationError : undefined,\n };\n });\n\n effect(onCleanup => {\n const formGroup = ɵsdCoerceFormGroup(options.form());\n const name = options.name();\n const control = options.control();\n\n if (!formGroup || !name) return;\n\n const current = formGroup.get(name);\n const ownsRegistration = !current;\n if (ownsRegistration) formGroup.addControl(name, control);\n\n onCleanup(() => {\n if (ownsRegistration && formGroup.get(name) === control) formGroup.removeControl(name);\n });\n });\n\n if (hasModelBinding(options)) {\n effect(() => {\n const control = options.control();\n const controlValue = readControlValue(options);\n\n untracked(() => {\n if (!controlEquals(control.value, controlValue)) {\n control.setValue(controlValue, { emitEvent: false });\n }\n });\n });\n }\n\n if (hasModelBinding(options)) {\n effect(onCleanup => {\n const control = options.control();\n const subscription = control.valueChanges.subscribe(controlValue => {\n writeModelValue(options, controlValue);\n });\n onCleanup(() => subscription.unsubscribe());\n });\n }\n\n if (options.validators || options.asyncValidators || options.required) {\n effect(onCleanup => {\n const control = options.control();\n const requestedValidators = normalizeValidatorList(options.validators?.());\n if (options.required?.()) requestedValidators.push(Validators.required);\n const validatorsToAdd = [...new Set(requestedValidators)].filter(validator => !control.hasValidator(validator));\n const asyncValidatorsToAdd = [...new Set(normalizeValidatorList(options.asyncValidators?.()))].filter(\n validator => !control.hasAsyncValidator(validator)\n );\n\n untracked(() => {\n if (validatorsToAdd.length > 0) control.addValidators(validatorsToAdd);\n if (asyncValidatorsToAdd.length > 0) control.addAsyncValidators(asyncValidatorsToAdd);\n control.updateValueAndValidity({ emitEvent: false });\n });\n\n onCleanup(() => {\n untracked(() => {\n if (validatorsToAdd.length > 0) control.removeValidators(validatorsToAdd);\n if (asyncValidatorsToAdd.length > 0) control.removeAsyncValidators(asyncValidatorsToAdd);\n control.updateValueAndValidity({ emitEvent: false });\n });\n });\n });\n }\n\n if (options.disabled) {\n effect(onCleanup => {\n const control = options.control();\n const disabled = !!options.disabled!();\n const previousDisabled = control.disabled;\n let appliedDisabled: boolean | undefined;\n\n untracked(() => {\n if (disabled !== control.disabled) {\n appliedDisabled = disabled;\n if (disabled) control.disable({ emitEvent: false });\n else control.enable({ emitEvent: false });\n }\n });\n\n onCleanup(() => {\n if (appliedDisabled === undefined || control.disabled !== appliedDisabled) return;\n\n untracked(() => {\n if (previousDisabled) control.disable({ emitEvent: false });\n else control.enable({ emitEvent: false });\n });\n });\n });\n }\n\n return {\n state,\n markAsTouched: () => options.control().markAsTouched(),\n markAsUntouched: () => options.control().markAsUntouched(),\n markAsDirty: () => options.control().markAsDirty(),\n markAsPristine: () => options.control().markAsPristine(),\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sdcorejs-angular-forms-models.mjs","sources":["../../../projects/sdcorejs-angular/forms/models/src/sd-form.configuration.ts","../../../projects/sdcorejs-angular/forms/models/src/sd-form-control.model.ts","../../../projects/sdcorejs-angular/forms/models/src/sd-custom-validator.model.ts","../../../projects/sdcorejs-angular/forms/models/src/form-control-state.ts","../../../projects/sdcorejs-angular/forms/models/src/sd-viewed.ts","../../../projects/sdcorejs-angular/forms/models/src/sd-form-control-connector.ts","../../../projects/sdcorejs-angular/forms/models/src/sd-strict-date-adapter.ts","../../../projects/sdcorejs-angular/forms/models/sdcorejs-angular-forms-models.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport { MatFormFieldAppearance } from '@angular/material/form-field';\n\nexport interface ISdFormConfiguration {\n appearance?: MatFormFieldAppearance;\n}\n\nexport const SD_FORM_CONFIGURATION = new InjectionToken<ISdFormConfiguration>('sd.form.configuration');\n","import { FormControl, AsyncValidatorFn, ValidatorFn, FormControlOptions, FormControlState } from '@angular/forms';\nimport { Subject } from 'rxjs';\n\nexport class SdFormControl extends FormControl {\n sdChanges: Subject<boolean> = new Subject<boolean>();\n untouchChanges: Subject<boolean> = new Subject<boolean>();\n touchChanges: Subject<boolean> = new Subject<boolean>();\n pristineChanges: Subject<boolean> = new Subject<boolean>();\n constructor(\n formState?: FormControlState<any>,\n validatorOrOpts?: ValidatorFn | ValidatorFn[] | FormControlOptions | null,\n asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null\n ) {\n super(formState, validatorOrOpts, asyncValidator);\n }\n\n override markAsUntouched(opts?: { onlySelf?: boolean; emitEvent?: boolean }): void {\n super.markAsUntouched(opts);\n this.untouchChanges.next(true);\n this.sdChanges.next(true);\n }\n\n override markAsTouched(opts?: { onlySelf?: boolean; emitEvent?: boolean }): void {\n super.markAsTouched(opts);\n this.touchChanges.next(true);\n this.sdChanges.next(true);\n }\n\n override markAsPristine(opts?: { onlySelf?: boolean; emitEvent?: boolean }): void {\n super.markAsPristine(opts);\n this.pristineChanges.next(true);\n this.sdChanges.next(true);\n }\n}\n","import { AbstractControl, AsyncValidatorFn, ValidatorFn } from '@angular/forms';\n\nexport type SdCustomValidator = (value: any) => string | Promise<string>;\n\n/**\n * Inline-error sentinel validator. Returns `{ inlineError: true }` so the form\n * template can render `<mat-error>{{ inlineError }}</mat-error>` whenever the\n * host component has a non-empty `[inlineError]` input. The error message\n * itself is read from the input — this validator only flags the state.\n *\n * why: each form component (input, textarea, select, checkbox, radio, switch,\n * date, datetime, input-number, autocomplete) previously declared the same\n * private `customInlineErrorValidator()` method. Centralized here so adding\n * another form component does not silently re-introduce the duplicate.\n */\nexport const SdInlineErrorValidator: ValidatorFn = (): Record<string, unknown> | null => ({ inlineError: true });\n\nexport const HandleSdCustomValidator = (func: SdCustomValidator): AsyncValidatorFn => {\n return async (c: AbstractControl): Promise<Record<string, any> | null> => {\n const value = c.value === 0 ? c.value : c.value || null;\n if (func && typeof func === 'function') {\n const result = func(value);\n if (result instanceof Promise) {\n const message = await result;\n if (message) {\n return {\n customValidator: message,\n };\n }\n return null;\n }\n if (result) {\n return {\n customValidator: result,\n };\n }\n return null;\n }\n return null;\n };\n};\n","import { DestroyRef, EffectRef, Signal, computed, effect, inject, signal, untracked } from '@angular/core';\nimport { AbstractControl } from '@angular/forms';\nimport { Subscription } from 'rxjs';\nimport { startWith } from 'rxjs/operators';\n\nexport interface SdFormControlSnapshot<T> {\n value: T | undefined;\n disabled: boolean;\n invalid: boolean;\n touched: boolean;\n}\n\n/**\n * Wrap an AbstractControl Signal into a reactive snapshot signal.\n *\n * Re-emits on every value, status, touched, or dirty change so\n * downstream consumers can derive `data-disabled`, `data-value`,\n * `data-empty`, and `data-invalid` host-binding attributes from a\n * single, lazily evaluated signal.\n *\n * \"invalid\" is intentionally gated on `touched || dirty` so validation\n * errors are not surfaced until the user has interacted with the field.\n *\n * Must be called inside an Angular injection context (constructor,\n * field initialiser, or `runInInjectionContext`).\n */\nexport function sdFormControlState<T = unknown>(control: Signal<AbstractControl<T> | null | undefined>): Signal<SdFormControlSnapshot<T>> {\n // A tick counter incremented on every control event (value, status,\n // touched, dirty). Written only from the effect below — never inside\n // a computed() — satisfying Angular's no-side-effect-in-reactive rule.\n const tick = signal(0);\n const destroyRef = inject(DestroyRef);\n\n // Holds the active RxJS subscription so it can be torn down when the\n // control instance changes or the host component is destroyed.\n let subscription: Subscription | null = null;\n\n // An effect re-runs whenever the control Signal changes, tears down the\n // old subscription and creates a fresh one for the incoming control.\n // `AbstractControl.events` (Angular 14+) covers value, status, touched,\n // and dirty changes — a superset of valueChanges + statusChanges.\n const effectRef: EffectRef = effect(() => {\n const c = control(); // tracked — effect re-runs on change\n // effect() runs asynchronously; the old subscription is torn down on the next\n // scheduled run, not synchronously on signal change. A one-tick window of\n // double-subscription is harmless due to computed() memoization.\n subscription?.unsubscribe();\n subscription = null;\n\n if (c) {\n subscription = c.events.pipe(startWith(null)).subscribe(() => {\n // Increment outside reactive context to avoid Angular's\n // \"signal written from computed / template\" error.\n untracked(() => tick.update(n => n + 1));\n });\n }\n });\n\n // Clean up effect and subscription when host component is destroyed.\n destroyRef.onDestroy(() => {\n effectRef.destroy();\n subscription?.unsubscribe();\n });\n\n return computed((): SdFormControlSnapshot<T> => {\n const _control = control();\n tick(); // reactive dependency — recomputes on every control event\n\n if (!_control) {\n return { value: undefined, disabled: false, invalid: false, touched: false };\n }\n\n return {\n value: _control.value as T,\n disabled: _control.disabled,\n invalid: _control.invalid && (_control.touched || _control.dirty),\n touched: _control.touched,\n };\n });\n}\n","import { booleanAttribute, computed, Signal } from '@angular/core';\n\n/**\n * Three display states shared by sd-form-controls:\n * - `false` → full edit chrome (input / dropdown).\n * - `true` → static read-only view (`<sd-view>` text), no editor.\n * - `'inline'` → the editor is STILL rendered (so its panel works), but its chrome is\n * hidden; the `<sd-view>` text is the visible face / trigger. Click the text to open\n * the picker's panel. The text is retained while the panel is open — it only changes\n * when a new value is committed (JIRA-style click-to-edit).\n */\nexport type SdViewed = boolean | 'inline';\nexport type SdViewedInput = SdViewed | '' | null | undefined;\n\n/**\n * `viewed` input transform. Keeps `booleanAttribute` coercion so a bare attribute\n * (`<sd-select viewed>`) still resolves to `true`, but intercepts the literal\n * `'inline'` first — `booleanAttribute('inline')` would otherwise coerce it to `true`.\n */\nexport function sdViewedTransform(v: SdViewedInput): SdViewed {\n return v === 'inline' ? 'inline' : booleanAttribute(v);\n}\n\nexport interface SdViewedInlineApi {\n /** `viewed() === 'inline'` — editor rendered but chrome hidden; sd-view text is the trigger face. */\n readonly isInline: Signal<boolean>;\n /** `viewed() === true` — static read-only view (no editor rendered). */\n readonly isViewed: Signal<boolean>;\n /** Open the picker from the inline text face. No-op unless `'inline'`. */\n enterInlineEdit(): void;\n}\n\n/**\n * Compose the tri-state `viewed` semantics into a control. `open` opens the control's\n * native picker (mat-select panel / mat-calendar / overlay). In `'inline'` mode the editor\n * is always rendered (chrome hidden via CSS), so `open()` can fire immediately on click —\n * no render-swap, the view text never disappears.\n *\n * @param viewed the control's `viewed` input signal.\n * @param open opens the control's picker; called by `enterInlineEdit`.\n * @param disabled the control's disabled state. why: a disabled `'inline'` field must behave\n * like `viewed=true` (static, NOT click-to-edit) — you can't edit a disabled control.\n */\nexport function sdViewedInline(viewed: Signal<SdViewed>, open?: () => void, disabled?: Signal<boolean>): SdViewedInlineApi {\n // why: disabled biến 'inline' thành static view (isInline=false, isViewed=true) — không cho sửa.\n const isInline = computed(() => viewed() === 'inline' && !disabled?.());\n const isViewed = computed(() => viewed() === true || (viewed() === 'inline' && !!disabled?.()));\n const enterInlineEdit = (): void => {\n if (isInline()) open?.();\n };\n return { isInline, isViewed, enterInlineEdit };\n}\n","import { Signal, computed, effect, untracked } from '@angular/core';\nimport { AbstractControl, AsyncValidatorFn, FormGroup, NgForm, ValidatorFn, Validators } from '@angular/forms';\n\nimport { sdFormControlState } from './form-control-state';\nimport { SdViewed } from './sd-viewed';\n\nexport type ɵSdFormControlParent = FormGroup | NgForm | { readonly form: unknown } | null | undefined;\n\ninterface ɵSdFormControlConnectorBaseOptions<TControl> {\n /** Parent form source. NgForm and wrapper values are unwrapped on every rebind. */\n readonly form: Signal<ɵSdFormControlParent>;\n /** Registration name. Empty names intentionally leave the control unregistered. */\n readonly name: Signal<string | null | undefined>;\n /** Canonical control registered in the parent form. */\n readonly control: Signal<AbstractControl<TControl>>;\n readonly validators?: Signal<ValidatorFn | readonly ValidatorFn[] | null | undefined>;\n readonly asyncValidators?: Signal<AsyncValidatorFn | readonly AsyncValidatorFn[] | null | undefined>;\n /** Adds/removes Validators.required while preserving validators supplied above. */\n readonly required?: Signal<boolean | null | undefined>;\n readonly disabled?: Signal<boolean | null | undefined>;\n /** UI-only read-only policy. This never disables the Angular control. */\n readonly readonly?: Signal<boolean | null | undefined>;\n /** Exact SDCoreJS display policy. This never disables the Angular control. */\n readonly viewed?: Signal<SdViewed | null | undefined>;\n /** Component-local validation message. Visibility is interaction-gated in state. */\n readonly validationError?: Signal<string | null | undefined>;\n}\n\ninterface ɵSdFormControlRegistrationOptions<TControl> extends ɵSdFormControlConnectorBaseOptions<TControl> {\n readonly model?: never;\n readonly writeModel?: never;\n readonly modelToControl?: never;\n readonly controlToModel?: never;\n readonly modelEquals?: never;\n readonly controlEquals?: never;\n}\n\ninterface ɵSdFormControlIdentityOptions<TValue> extends ɵSdFormControlConnectorBaseOptions<TValue> {\n readonly model: Signal<TValue>;\n readonly writeModel: (value: TValue) => void;\n readonly modelToControl?: never;\n readonly controlToModel?: never;\n readonly modelEquals?: (left: TValue, right: TValue) => boolean;\n readonly controlEquals?: (left: TValue, right: TValue) => boolean;\n}\n\ninterface ɵSdFormControlAdaptedOptions<TModel, TControl> extends ɵSdFormControlConnectorBaseOptions<TControl> {\n readonly model: Signal<TModel>;\n readonly writeModel: (value: TModel) => void;\n readonly modelToControl: (value: TModel) => TControl;\n readonly controlToModel: (value: TControl) => TModel;\n readonly modelEquals?: (left: TModel, right: TModel) => boolean;\n readonly controlEquals?: (left: TControl, right: TControl) => boolean;\n}\n\ntype ɵSdTypesExactlyMatch<TLeft, TRight> = [TLeft] extends [TRight] ? ([TRight] extends [TLeft] ? true : false) : false;\n\n/**\n * @internal Unstable connector contract for cross-entrypoint SDCoreJS controls.\n * Consumers must use registration-only, same-type identity binding, or provide\n * both adapters when model and control representations differ.\n */\nexport type ɵSdFormControlConnectorOptions<TModel, TControl> =\n | ɵSdFormControlRegistrationOptions<TControl>\n | ɵSdFormControlAdaptedOptions<TModel, TControl>\n | (ɵSdTypesExactlyMatch<TModel, TControl> extends true ? ɵSdFormControlIdentityOptions<TModel> : never);\n\ntype ɵSdFormControlBindingOptions<TModel, TControl> =\n | ɵSdFormControlIdentityOptions<TControl>\n | ɵSdFormControlAdaptedOptions<TModel, TControl>;\n\ntype ɵSdFormControlImplementationOptions<TModel, TControl> =\n | ɵSdFormControlRegistrationOptions<TControl>\n | ɵSdFormControlBindingOptions<TModel, TControl>;\n\nexport interface ɵSdFormControlConnectorState<TControl> {\n readonly value: TControl | undefined;\n readonly disabled: boolean;\n readonly invalid: boolean;\n readonly touched: boolean;\n readonly dirty: boolean;\n readonly required: boolean;\n readonly readonly: boolean;\n readonly viewed: SdViewed;\n readonly isViewed: boolean;\n readonly isInline: boolean;\n readonly showValidationError: boolean;\n readonly validationError: string | undefined;\n}\n\nexport interface ɵSdFormControlConnector<TControl = unknown> {\n readonly state: Signal<ɵSdFormControlConnectorState<TControl>>;\n markAsTouched(): void;\n markAsUntouched(): void;\n markAsDirty(): void;\n markAsPristine(): void;\n}\n\n/** Coerces the form shapes accepted by SDCoreJS controls into one FormGroup. */\nexport function ɵsdCoerceFormGroup(value: unknown): FormGroup | undefined {\n if (value instanceof NgForm) return value.form;\n if (value instanceof FormGroup) return value;\n if (typeof value !== 'object' || value === null || !('form' in value)) return undefined;\n\n const wrappedForm = (value as { readonly form: unknown }).form;\n return wrappedForm instanceof FormGroup ? wrappedForm : undefined;\n}\n\nfunction normalizeValidatorList<TValidator extends ValidatorFn | AsyncValidatorFn>(\n value: TValidator | readonly TValidator[] | null | undefined\n): TValidator[] {\n if (!value) return [];\n return typeof value === 'function' ? [value] : [...value];\n}\n\nfunction hasModelBinding<TModel, TControl>(\n options: ɵSdFormControlImplementationOptions<TModel, TControl>\n): options is ɵSdFormControlBindingOptions<TModel, TControl> {\n return options.model !== undefined && options.writeModel !== undefined;\n}\n\nfunction readControlValue<TModel, TControl>(options: ɵSdFormControlBindingOptions<TModel, TControl>): TControl {\n if (options.modelToControl) return options.modelToControl(options.model());\n return options.model();\n}\n\nfunction writeModelValue<TModel, TControl>(options: ɵSdFormControlBindingOptions<TModel, TControl>, controlValue: TControl): void {\n if (options.controlToModel) {\n const modelValue = options.controlToModel(controlValue);\n if (!(options.modelEquals ?? Object.is)(options.model(), modelValue)) options.writeModel(modelValue);\n return;\n }\n\n if (!(options.modelEquals ?? Object.is)(options.model(), controlValue)) options.writeModel(controlValue);\n}\n\n/**\n * Connects the signal-based SDCoreJS model contract to an Angular control.\n * Registration and subscriptions are rebound transactionally, and cleanup only\n * removes a control while the connector still owns that exact registration.\n */\nexport function ɵsdFormControlConnector<TModel, TControl>(\n options: ɵSdFormControlConnectorOptions<TModel, TControl>\n): ɵSdFormControlConnector<TControl>;\nexport function ɵsdFormControlConnector<TModel, TControl>(\n options: ɵSdFormControlImplementationOptions<TModel, TControl>\n): ɵSdFormControlConnector<TControl> {\n const controlEquals = options.controlEquals ?? Object.is;\n const controlState = sdFormControlState(options.control);\n const state = computed((): ɵSdFormControlConnectorState<TControl> => {\n const snapshot = controlState();\n const required = !!options.required?.();\n const readonly = !!options.readonly?.();\n const viewed = options.viewed?.() ?? false;\n const validationError = options.validationError?.() || undefined;\n const showValidationError = snapshot.invalid && validationError !== undefined;\n\n return {\n ...snapshot,\n dirty: options.control().dirty,\n required,\n readonly,\n viewed,\n isViewed: viewed === true,\n isInline: viewed === 'inline',\n showValidationError,\n validationError: showValidationError ? validationError : undefined,\n };\n });\n\n effect(onCleanup => {\n const formGroup = ɵsdCoerceFormGroup(options.form());\n const name = options.name();\n const control = options.control();\n\n if (!formGroup || !name) return;\n\n const current = formGroup.get(name);\n const ownsRegistration = !current;\n if (ownsRegistration) formGroup.addControl(name, control);\n\n onCleanup(() => {\n if (ownsRegistration && formGroup.get(name) === control) formGroup.removeControl(name);\n });\n });\n\n if (hasModelBinding(options)) {\n effect(() => {\n const control = options.control();\n const controlValue = readControlValue(options);\n\n untracked(() => {\n if (!controlEquals(control.value, controlValue)) {\n control.setValue(controlValue, { emitEvent: false });\n }\n });\n });\n }\n\n if (hasModelBinding(options)) {\n effect(onCleanup => {\n const control = options.control();\n const subscription = control.valueChanges.subscribe(controlValue => {\n writeModelValue(options, controlValue);\n });\n onCleanup(() => subscription.unsubscribe());\n });\n }\n\n if (options.validators || options.asyncValidators || options.required) {\n effect(onCleanup => {\n const control = options.control();\n const requestedValidators = normalizeValidatorList(options.validators?.());\n if (options.required?.()) requestedValidators.push(Validators.required);\n const validatorsToAdd = [...new Set(requestedValidators)].filter(validator => !control.hasValidator(validator));\n const asyncValidatorsToAdd = [...new Set(normalizeValidatorList(options.asyncValidators?.()))].filter(\n validator => !control.hasAsyncValidator(validator)\n );\n\n untracked(() => {\n if (validatorsToAdd.length > 0) control.addValidators(validatorsToAdd);\n if (asyncValidatorsToAdd.length > 0) control.addAsyncValidators(asyncValidatorsToAdd);\n control.updateValueAndValidity({ emitEvent: false });\n });\n\n onCleanup(() => {\n untracked(() => {\n if (validatorsToAdd.length > 0) control.removeValidators(validatorsToAdd);\n if (asyncValidatorsToAdd.length > 0) control.removeAsyncValidators(asyncValidatorsToAdd);\n control.updateValueAndValidity({ emitEvent: false });\n });\n });\n });\n }\n\n if (options.disabled) {\n effect(onCleanup => {\n const control = options.control();\n const disabled = !!options.disabled!();\n const previousDisabled = control.disabled;\n let appliedDisabled: boolean | undefined;\n\n untracked(() => {\n if (disabled !== control.disabled) {\n appliedDisabled = disabled;\n if (disabled) control.disable({ emitEvent: false });\n else control.enable({ emitEvent: false });\n }\n });\n\n onCleanup(() => {\n if (appliedDisabled === undefined || control.disabled !== appliedDisabled) return;\n\n untracked(() => {\n if (previousDisabled) control.disable({ emitEvent: false });\n else control.enable({ emitEvent: false });\n });\n });\n });\n }\n\n return {\n state,\n markAsTouched: () => options.control().markAsTouched(),\n markAsUntouched: () => options.control().markAsUntouched(),\n markAsDirty: () => options.control().markAsDirty(),\n markAsPristine: () => options.control().markAsPristine(),\n };\n}\n","import { Injectable, Provider } from '@angular/core';\nimport { DateFnsAdapter, provideDateFnsAdapter } from '@angular/material-date-fns-adapter';\nimport { DateAdapter, MatDateFormats } from '@angular/material/core';\nimport { isValid as isValidDate, parse as parseDate } from 'date-fns';\n\n/**\n * DateAdapter từ chối mọi chuỗi người dùng chưa gõ xong.\n *\n * WHY cần đến mức này: `<input matInput [matDatepicker]>` khiến Material tự\n * `parse()` lại ô nhập SAU MỖI PHÍM GÕ rồi ghi thẳng kết quả vào form control.\n * Adapter mặc định quá dễ dãi trên hai đường:\n *\n * 1. `date-fns.parse` nhận năm thiếu chữ số → `11/12/2` thành năm 0002,\n * `11/12/20` thành năm 0020. Ô nhập vừa gõ dở đã bị coi là hợp lệ (cờ lỗi\n * bị xoá) và control ôm một ngày rác.\n * 2. `parseISO` coi chuỗi 2 chữ số là thế kỷ → xoá lùi còn `11` ra năm 1100.\n *\n * Cả hai đường đều bị chặn ở đây: control chỉ nhận giá trị khi người dùng thực\n * sự gõ xong.\n */\n@Injectable()\nexport class SdStrictDateFnsAdapter extends DateFnsAdapter {\n override parse(value: unknown, parseFormat: string | string[]): Date | null {\n if (typeof value !== 'string') return super.parse(value, parseFormat);\n\n const trimmed = value.trim();\n if (!trimmed) return null;\n\n // Cố tình KHÔNG gọi super.parse: chỗ đó có ISO fallback (đường số 2 ở trên).\n for (const currentFormat of Array.isArray(parseFormat) ? parseFormat : [parseFormat]) {\n const parsed = parseDate(trimmed, currentFormat, new Date(), { locale: this.locale });\n if (!isValidDate(parsed)) continue;\n // Round-trip: chuỗi phải khớp 1-1 với format, nên năm thiếu chữ số bị loại.\n if (this.format(parsed, currentFormat) === trimmed) return parsed;\n }\n\n return null;\n }\n}\n\n/** `provideDateFnsAdapter` + ép dùng adapter parse chặt ở trên. */\nexport function provideSdStrictDateFnsAdapter(formats: MatDateFormats): Provider[] {\n return [\n provideDateFnsAdapter(formats),\n // Phải đứng SAU provideDateFnsAdapter để ghi đè DateAdapter mà nó đăng ký.\n { provide: DateAdapter, useClass: SdStrictDateFnsAdapter },\n ];\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["parseDate","isValidDate"],"mappings":"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"}
@@ -503,6 +503,8 @@ const EN_MESSAGES = {
503
503
  'core.component.table.choose-filter-hint': 'Please choose a filter to start',
504
504
  'core.component.table.no-data': 'No data',
505
505
  'core.component.table.export-excel': 'Export Excel',
506
+ 'core.component.table.exporting': 'Exporting...{percent}%',
507
+ 'core.component.table.export': 'Export',
506
508
  'core.component.table.export-csv': 'Export CSV',
507
509
  'core.component.table.showing': 'Showing:',
508
510
  'core.component.table.paginator.first-page': 'First page',
@@ -538,7 +540,11 @@ const EN_MESSAGES = {
538
540
  // ---- Module: layout (sidebar / user menu) ----
539
541
  'core.module.layout.user.change-password': 'Change password',
540
542
  'core.module.layout.user.logout': 'Sign out',
543
+ 'core.module.layout.user.update-profile': 'Update profile',
544
+ 'core.module.layout.user.setting': 'Settings',
545
+ 'core.module.layout.user.notification': 'Notifications',
541
546
  'core.module.layout.sidebar.search': 'Search',
547
+ 'core.module.layout.sidebar.toggle': 'Toggle sidebar',
542
548
  // ---- Module: layout (greeting + weekday) ----
543
549
  'core.module.layout.greeting.hello': 'Hello, {name}',
544
550
  'core.module.layout.weekday.0': 'Sunday',
@@ -559,6 +565,8 @@ const EN_MESSAGES = {
559
565
  'core.module.layout.not-found.back': 'Go back',
560
566
  // ---- Module: layout (home page) ----
561
567
  'core.module.layout.home.tab-name': 'Home',
568
+ 'core.module.layout.not-found.tab-name': 'Page Not Found',
569
+ 'core.module.layout.forbidden.tab-name': 'Access Denied',
562
570
  'core.module.layout.home.feature.data': 'Data',
563
571
  'core.module.layout.home.feature.reports': 'Reports',
564
572
  'core.module.layout.home.feature.users': 'Users',
@@ -1068,6 +1076,8 @@ const JA_MESSAGES = {
1068
1076
  'core.component.table.choose-filter-hint': 'まずフィルターを選択してください',
1069
1077
  'core.component.table.no-data': 'データがありません',
1070
1078
  'core.component.table.export-excel': 'Excelエクスポート',
1079
+ 'core.component.table.exporting': 'エクスポート中...{percent}%',
1080
+ 'core.component.table.export': 'エクスポート',
1071
1081
  'core.component.table.export-csv': 'CSVエクスポート',
1072
1082
  'core.component.table.showing': '表示中:',
1073
1083
  'core.component.table.paginator.first-page': '最初のページ',
@@ -1103,7 +1113,11 @@ const JA_MESSAGES = {
1103
1113
  // ---- Module: layout (sidebar / user menu) ----
1104
1114
  'core.module.layout.user.change-password': 'パスワード変更',
1105
1115
  'core.module.layout.user.logout': 'ログアウト',
1116
+ 'core.module.layout.user.update-profile': 'プロフィールを更新',
1117
+ 'core.module.layout.user.setting': '設定',
1118
+ 'core.module.layout.user.notification': '通知',
1106
1119
  'core.module.layout.sidebar.search': '検索',
1120
+ 'core.module.layout.sidebar.toggle': 'サイドバーを切り替える',
1107
1121
  // ---- Module: layout (greeting + weekday) ----
1108
1122
  'core.module.layout.greeting.hello': 'こんにちは、{name}さん',
1109
1123
  'core.module.layout.weekday.0': '日曜日',
@@ -1124,6 +1138,8 @@ const JA_MESSAGES = {
1124
1138
  'core.module.layout.not-found.back': '戻る',
1125
1139
  // ---- Module: layout (home page) ----
1126
1140
  'core.module.layout.home.tab-name': 'ホーム',
1141
+ 'core.module.layout.not-found.tab-name': 'ページが見つかりません',
1142
+ 'core.module.layout.forbidden.tab-name': 'アクセス拒否',
1127
1143
  'core.module.layout.home.feature.data': 'データ',
1128
1144
  'core.module.layout.home.feature.reports': 'レポート',
1129
1145
  'core.module.layout.home.feature.users': 'ユーザー',
@@ -1633,6 +1649,8 @@ const KO_MESSAGES = {
1633
1649
  'core.component.table.choose-filter-hint': '시작하려면 필터를 선택해 주세요',
1634
1650
  'core.component.table.no-data': '데이터가 없습니다',
1635
1651
  'core.component.table.export-excel': 'Excel 내보내기',
1652
+ 'core.component.table.exporting': '내보내는 중...{percent}%',
1653
+ 'core.component.table.export': '내보내기',
1636
1654
  'core.component.table.export-csv': 'CSV 내보내기',
1637
1655
  'core.component.table.showing': '표시 중:',
1638
1656
  'core.component.table.paginator.first-page': '첫 페이지',
@@ -1668,7 +1686,11 @@ const KO_MESSAGES = {
1668
1686
  // ---- Module: layout (sidebar / user menu) ----
1669
1687
  'core.module.layout.user.change-password': '비밀번호 변경',
1670
1688
  'core.module.layout.user.logout': '로그아웃',
1689
+ 'core.module.layout.user.update-profile': '프로필 수정',
1690
+ 'core.module.layout.user.setting': '설정',
1691
+ 'core.module.layout.user.notification': '알림',
1671
1692
  'core.module.layout.sidebar.search': '검색',
1693
+ 'core.module.layout.sidebar.toggle': '사이드바 전환',
1672
1694
  // ---- Module: layout (greeting + weekday) ----
1673
1695
  'core.module.layout.greeting.hello': '안녕하세요, {name}님',
1674
1696
  'core.module.layout.weekday.0': '일요일',
@@ -1689,6 +1711,8 @@ const KO_MESSAGES = {
1689
1711
  'core.module.layout.not-found.back': '돌아가기',
1690
1712
  // ---- Module: layout (home page) ----
1691
1713
  'core.module.layout.home.tab-name': '홈',
1714
+ 'core.module.layout.not-found.tab-name': '페이지를 찾을 수 없음',
1715
+ 'core.module.layout.forbidden.tab-name': '접근 거부',
1692
1716
  'core.module.layout.home.feature.data': '데이터',
1693
1717
  'core.module.layout.home.feature.reports': '리포트',
1694
1718
  'core.module.layout.home.feature.users': '사용자',
@@ -2198,6 +2222,8 @@ const VI_MESSAGES = {
2198
2222
  'core.component.table.choose-filter-hint': 'Vui lòng chọn bộ lọc để bắt đầu',
2199
2223
  'core.component.table.no-data': 'Chưa có dữ liệu',
2200
2224
  'core.component.table.export-excel': 'Xuất excel',
2225
+ 'core.component.table.exporting': 'Đang xuất...{percent}%',
2226
+ 'core.component.table.export': 'Xuất dữ liệu',
2201
2227
  'core.component.table.export-csv': 'Xuất CSV',
2202
2228
  'core.component.table.showing': 'Đang hiển thị:',
2203
2229
  'core.component.table.paginator.first-page': 'Trang đầu',
@@ -2233,7 +2259,11 @@ const VI_MESSAGES = {
2233
2259
  // ---- Module: layout (sidebar / user menu) ----
2234
2260
  'core.module.layout.user.change-password': 'Đổi mật khẩu',
2235
2261
  'core.module.layout.user.logout': 'Đăng xuất',
2262
+ 'core.module.layout.user.update-profile': 'Chỉnh sửa hồ sơ',
2263
+ 'core.module.layout.user.setting': 'Thiết lập',
2264
+ 'core.module.layout.user.notification': 'Thông báo',
2236
2265
  'core.module.layout.sidebar.search': 'Tìm kiếm',
2266
+ 'core.module.layout.sidebar.toggle': 'Thu gọn hoặc mở rộng thanh điều hướng',
2237
2267
  // ---- Module: layout (greeting + weekday) ----
2238
2268
  'core.module.layout.greeting.hello': 'Xin chào, {name}',
2239
2269
  'core.module.layout.weekday.0': 'Chủ Nhật',
@@ -2254,6 +2284,8 @@ const VI_MESSAGES = {
2254
2284
  'core.module.layout.not-found.back': 'Quay trở lại',
2255
2285
  // ---- Module: layout (home page) ----
2256
2286
  'core.module.layout.home.tab-name': 'Trang chủ',
2287
+ 'core.module.layout.not-found.tab-name': 'Không tìm thấy trang',
2288
+ 'core.module.layout.forbidden.tab-name': 'Không có quyền',
2257
2289
  'core.module.layout.home.feature.data': 'Dữ liệu',
2258
2290
  'core.module.layout.home.feature.reports': 'Báo cáo',
2259
2291
  'core.module.layout.home.feature.users': 'Người dùng',
@@ -2763,6 +2795,8 @@ const ZH_MESSAGES = {
2763
2795
  'core.component.table.choose-filter-hint': '请先选择筛选条件',
2764
2796
  'core.component.table.no-data': '暂无数据',
2765
2797
  'core.component.table.export-excel': '导出 Excel',
2798
+ 'core.component.table.exporting': '正在导出...{percent}%',
2799
+ 'core.component.table.export': '导出数据',
2766
2800
  'core.component.table.export-csv': '导出 CSV',
2767
2801
  'core.component.table.showing': '显示:',
2768
2802
  'core.component.table.paginator.first-page': '首页',
@@ -2798,7 +2832,11 @@ const ZH_MESSAGES = {
2798
2832
  // ---- Module: layout (sidebar / user menu) ----
2799
2833
  'core.module.layout.user.change-password': '修改密码',
2800
2834
  'core.module.layout.user.logout': '退出登录',
2835
+ 'core.module.layout.user.update-profile': '更新个人资料',
2836
+ 'core.module.layout.user.setting': '设置',
2837
+ 'core.module.layout.user.notification': '通知',
2801
2838
  'core.module.layout.sidebar.search': '搜索',
2839
+ 'core.module.layout.sidebar.toggle': '切换侧边栏',
2802
2840
  // ---- Module: layout (greeting + weekday) ----
2803
2841
  'core.module.layout.greeting.hello': '你好,{name}',
2804
2842
  'core.module.layout.weekday.0': '星期日',
@@ -2819,6 +2857,8 @@ const ZH_MESSAGES = {
2819
2857
  'core.module.layout.not-found.back': '返回',
2820
2858
  // ---- Module: layout (home page) ----
2821
2859
  'core.module.layout.home.tab-name': '首页',
2860
+ 'core.module.layout.not-found.tab-name': '页面未找到',
2861
+ 'core.module.layout.forbidden.tab-name': '拒绝访问',
2822
2862
  'core.module.layout.home.feature.data': '数据',
2823
2863
  'core.module.layout.home.feature.reports': '报表',
2824
2864
  'core.module.layout.home.feature.users': '用户',