@nexterajs/ui 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/nexterajs-ui-core.mjs +189 -5
- package/fesm2022/nexterajs-ui-core.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-dropdowns.mjs +314 -50
- package/fesm2022/nexterajs-ui-dropdowns.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-file-uploader.mjs +6 -3
- package/fesm2022/nexterajs-ui-file-uploader.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-forms.mjs +729 -370
- package/fesm2022/nexterajs-ui-forms.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-grid.mjs +1188 -208
- package/fesm2022/nexterajs-ui-grid.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-intl.mjs +15 -5
- package/fesm2022/nexterajs-ui-intl.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-notify.mjs +351 -0
- package/fesm2022/nexterajs-ui-notify.mjs.map +1 -0
- package/fesm2022/nexterajs-ui-pivot.mjs +146 -116
- package/fesm2022/nexterajs-ui-pivot.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-styling.mjs +568 -3
- package/fesm2022/nexterajs-ui-styling.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-tree-view.mjs +147 -18
- package/fesm2022/nexterajs-ui-tree-view.mjs.map +1 -1
- package/fesm2022/nexterajs-ui-validation.mjs +247 -92
- package/fesm2022/nexterajs-ui-validation.mjs.map +1 -1
- package/fesm2022/nexterajs-ui.mjs +1 -0
- package/fesm2022/nexterajs-ui.mjs.map +1 -1
- package/package.json +5 -1
- package/styles/_dropdown.scss +149 -0
- package/styles/_editor.scss +23 -4
- package/styles/_icons.scss +92 -0
- package/styles/_mixins.scss +104 -1
- package/styles/_scrollbar.scss +134 -0
- package/styles/_toast.scss +243 -0
- package/styles/_tokens.scss +48 -0
- package/styles/index.scss +4 -0
- package/types/nexterajs-ui-core.d.ts +142 -22
- package/types/nexterajs-ui-dropdowns.d.ts +145 -27
- package/types/nexterajs-ui-file-uploader.d.ts +4 -1
- package/types/nexterajs-ui-forms.d.ts +103 -19
- package/types/nexterajs-ui-grid.d.ts +445 -57
- package/types/nexterajs-ui-notify.d.ts +147 -0
- package/types/nexterajs-ui-pivot.d.ts +35 -22
- package/types/nexterajs-ui-styling.d.ts +163 -3
- package/types/nexterajs-ui-tree-view.d.ts +50 -1
- package/types/nexterajs-ui-validation.d.ts +94 -15
- package/types/nexterajs-ui.d.ts +1 -0
|
@@ -13,6 +13,8 @@ import { InjectionToken } from '@angular/core';
|
|
|
13
13
|
//
|
|
14
14
|
// Tokens: `#` optional digit, `0` required digit, `,` enable grouping (size 3), `.` decimal point
|
|
15
15
|
// (count of `0` after it = min fraction digits, count of `0`+`#` = max), `'…'` verbatim literal.
|
|
16
|
+
/** dx's cap on the significant digits a formatted number carries (ldml/number.js:21). */
|
|
17
|
+
const MAXIMUM_NUMBER_LENGTH = 15;
|
|
16
18
|
/** Parse an LDML pattern into a {@link NxeParsedNumberFormat}, or `null` for raw (no format). */
|
|
17
19
|
function parseNumberFormat(pattern) {
|
|
18
20
|
if (!pattern) {
|
|
@@ -118,16 +120,24 @@ function formatNumber(value, fmt, separators = NXE_EN_US_SEPARATORS) {
|
|
|
118
120
|
return String(value);
|
|
119
121
|
}
|
|
120
122
|
const negative = value < 0;
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
+
const abs = Math.abs(value);
|
|
124
|
+
// dx MAXIMUM_NUMBER_LENGTH (ldml/number.js:21, :152-153): the fraction gets only what is left
|
|
125
|
+
// of 15 significant digits after the integer part, `fitIntoRange(maxFloat, 0, 15 - intLen)`.
|
|
126
|
+
// Without it a 15-place mask printed 10/3 % as 3.333333333333333 rather than dx's
|
|
127
|
+
// 3.33333333333333.
|
|
128
|
+
const frac = Math.min(fmt.maxFrac, Math.max(0, MAXIMUM_NUMBER_LENGTH - String(Math.floor(abs)).length));
|
|
129
|
+
const rounded = roundHalfAwayFromZero(abs, frac);
|
|
130
|
+
const fixed = toDecimalString(rounded, frac);
|
|
123
131
|
const parts = fixed.split('.');
|
|
124
132
|
let intStr = parts[0];
|
|
125
133
|
let fracStr = parts[1] ?? '';
|
|
126
134
|
if (fmt.maxFrac > fmt.minFrac) {
|
|
127
135
|
fracStr = fracStr.replace(/0+$/, '');
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
}
|
|
137
|
+
// Always, not only for optional digits: the cap can leave fewer places than the mask requires,
|
|
138
|
+
// and dx's normalizeValueString pads those back with zeros.
|
|
139
|
+
while (fracStr.length < fmt.minFrac) {
|
|
140
|
+
fracStr += '0';
|
|
131
141
|
}
|
|
132
142
|
while (intStr.length < fmt.minInt) {
|
|
133
143
|
intStr = '0' + intStr;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nexterajs-ui-intl.mjs","sources":["../../../projects/nextera-ui/intl/src/lib/number-box/nxe-number-format.ts","../../../projects/nextera-ui/intl/src/lib/date-box/nxe-date-format.ts","../../../projects/nextera-ui/intl/src/lib/number-box/nxe-number-locale.ts","../../../projects/nextera-ui/intl/src/public-api.ts","../../../projects/nextera-ui/intl/src/nexterajs-ui-intl.ts"],"sourcesContent":["// DevExtreme LDML-style number format engine for nxe-number-box.\r\n//\r\n// Supports the patterns actually used in this codebase:\r\n// #,##0.00 — grouping, exactly 2 fraction digits\r\n// #,##0.00000 — grouping, exactly 5 fraction digits\r\n// #,##0.#####'%' — grouping, 0–5 fraction digits, literal '%' suffix (NO ×100)\r\n// #0 — integer, no grouping\r\n// #,##0 — integer with grouping\r\n// (empty) — raw number, no grouping\r\n//\r\n// Tokens: `#` optional digit, `0` required digit, `,` enable grouping (size 3), `.` decimal point\r\n// (count of `0` after it = min fraction digits, count of `0`+`#` = max), `'…'` verbatim literal.\r\n\r\nexport interface NxeParsedNumberFormat {\r\n prefix: string;\r\n suffix: string;\r\n useGrouping: boolean;\r\n /**\r\n * Digits per group, read from the pattern: `#,##0` is 3, `#,#0` is 2. Optional because\r\n * callers construct this shape by hand as well as through {@link parseNumberFormat};\r\n * omitted means dx's usual 3.\r\n */\r\n groupSize?: number;\r\n minInt: number;\r\n minFrac: number;\r\n maxFrac: number;\r\n}\r\n\r\n/** Parse an LDML pattern into a {@link NxeParsedNumberFormat}, or `null` for raw (no format). */\r\nexport function parseNumberFormat(pattern: string | undefined | null): NxeParsedNumberFormat | null {\r\n if (!pattern) {\r\n return null;\r\n }\r\n let prefix = '';\r\n let suffix = '';\r\n let core = '';\r\n let inQuote = false;\r\n let seenCore = false;\r\n for (const ch of pattern) {\r\n if (ch === \"'\") {\r\n inQuote = !inQuote;\r\n continue;\r\n }\r\n const isToken = !inQuote && (ch === '#' || ch === '0' || ch === ',' || ch === '.');\r\n if (isToken) {\r\n core += ch;\r\n seenCore = true;\r\n } else if (!seenCore) {\r\n prefix += ch;\r\n } else {\r\n suffix += ch;\r\n }\r\n }\r\n if (!core) {\r\n return null;\r\n }\r\n const dot = core.indexOf('.');\r\n const intPart = dot === -1 ? core : core.slice(0, dot);\r\n const fracPart = dot === -1 ? '' : core.slice(dot + 1);\r\n const count = (s: string, re: RegExp) => (s.match(re) || []).length;\r\n // The grouping SIZE comes from the pattern, not from a constant 3: dx reads the digits after\r\n // the last comma, so `#,##0` groups by 3 and `#,#0` groups by 2 (which renders 1234.5 as\r\n // \"12,34.5\"). Several consumer columns use the latter, most likely by accident - but it is\r\n // what their screens show today, so parity means reproducing it.\r\n const useGrouping = intPart.includes(',');\r\n const groupSize = useGrouping\r\n ? Math.max(1, count(intPart.slice(intPart.lastIndexOf(',') + 1), /[#0]/g))\r\n : 3;\r\n return {\r\n prefix,\r\n suffix,\r\n useGrouping,\r\n groupSize,\r\n minInt: Math.max(1, count(intPart, /0/g)),\r\n minFrac: count(fracPart, /0/g),\r\n maxFrac: count(fracPart, /[#0]/g),\r\n };\r\n}\r\n\r\n/**\r\n * Insert `sep` every `size` digits from the right.\r\n *\r\n * Written as a loop rather than a dynamic RegExp on purpose: the group size varies with the\r\n * pattern, and building a regex by interpolating into a template literal puts `\\B` and `\\d` one\r\n * missing backslash away from silently matching nothing.\r\n */\r\nfunction groupDigits(digits: string, size: number, sep: string): string {\r\n let out = '';\r\n for (let i = 0; i < digits.length; i++) {\r\n if (i > 0 && (digits.length - i) % size === 0) {\r\n out += sep;\r\n }\r\n out += digits[i];\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Render `value` with exactly `frac` fraction digits, from its SHORTEST decimal form.\r\n *\r\n * `toFixed` expands the binary value instead, so 1234567.891 at 14 digits becomes\r\n * \"1234567.89100000006147\" — the true double, and not what dx prints. Taking `String(value)`\r\n * (the shortest round-tripping decimal) and padding matches dx and is what a reader expects.\r\n * Exponential notation, which `String` produces below 1e-6, falls back to `toFixed`.\r\n */\r\nfunction toDecimalString(value: number, frac: number): string {\r\n const s = String(value);\r\n if (s.includes('e') || s.includes('E')) {\r\n return value.toFixed(frac);\r\n }\r\n const dot = s.indexOf('.');\r\n const intPart = dot === -1 ? s : s.slice(0, dot);\r\n const fracPart = dot === -1 ? '' : s.slice(dot + 1);\r\n return frac === 0 ? intPart : `${intPart}.${fracPart.slice(0, frac).padEnd(frac, '0')}`;\r\n}\r\n\r\n/** Round half-away-from-zero to `frac` fraction digits (DevExtreme rounding), fp-safe. */\r\nexport function roundHalfAwayFromZero(value: number, frac: number): number {\r\n const factor = Math.pow(10, frac);\r\n const rounded = Math.round(Math.abs(value) * factor + 1e-9) / factor;\r\n return value < 0 ? -rounded : rounded;\r\n}\r\n\r\n/** Format a number with the parsed pattern. `null`/`NaN` → `''`. */\r\n/**\r\n * The characters that separate groups and the fraction.\r\n *\r\n * The LDML pattern always writes `,` for grouping and `.` for the decimal point — that is the\r\n * pattern language, not the output. Which characters actually get rendered is a locale decision,\r\n * and it is the one that split this file: one consuming app needs `1,234.50`, another needs\r\n * `1.250.000,50`, and hardcoding either forces the other to fork.\r\n */\r\nexport interface NxeNumberSeparators {\r\n readonly group: string;\r\n readonly decimal: string;\r\n}\r\n\r\n/** DevExtreme's default, and this library's. */\r\nexport const NXE_EN_US_SEPARATORS: NxeNumberSeparators = { group: ',', decimal: '.' };\r\n\r\n/** Indonesian house style, used by several of the consuming applications. */\r\nexport const NXE_ID_SEPARATORS: NxeNumberSeparators = { group: '.', decimal: ',' };\r\n\r\nexport function formatNumber(\r\n value: number | null | undefined,\r\n fmt: NxeParsedNumberFormat | null,\r\n separators: NxeNumberSeparators = NXE_EN_US_SEPARATORS,\r\n): string {\r\n if (value == null || typeof value !== 'number' || isNaN(value)) {\r\n return '';\r\n }\r\n if (!fmt) {\r\n return String(value);\r\n }\r\n const negative = value < 0;\r\n const rounded = roundHalfAwayFromZero(Math.abs(value), fmt.maxFrac);\r\n const fixed = toDecimalString(rounded, fmt.maxFrac);\r\n const parts = fixed.split('.');\r\n let intStr = parts[0];\r\n let fracStr = parts[1] ?? '';\r\n if (fmt.maxFrac > fmt.minFrac) {\r\n fracStr = fracStr.replace(/0+$/, '');\r\n while (fracStr.length < fmt.minFrac) {\r\n fracStr += '0';\r\n }\r\n }\r\n while (intStr.length < fmt.minInt) {\r\n intStr = '0' + intStr;\r\n }\r\n if (fmt.useGrouping) {\r\n intStr = groupDigits(intStr, fmt.groupSize ?? 3, separators.group);\r\n }\r\n const body = fracStr ? `${intStr}${separators.decimal}${fracStr}` : intStr;\r\n return `${fmt.prefix}${negative ? '-' : ''}${body}${fmt.suffix}`;\r\n}\r\n\r\n/** Parse a (possibly formatted) string into a number; strips grouping/literals. Empty → `null`. */\r\nexport function parseNumber(\r\n text: string | null | undefined,\r\n separators: NxeNumberSeparators = NXE_EN_US_SEPARATORS,\r\n): number | null {\r\n if (text == null) {\r\n return null;\r\n }\r\n // Strip everything that is not a digit, a sign, or one of this locale's separators, then\r\n // drop the group separator and normalise the decimal one to `.` for parseFloat.\r\n const keep = new Set([separators.group, separators.decimal]);\r\n let cleaned = '';\r\n for (const ch of text) {\r\n if ((ch >= '0' && ch <= '9') || ch === '-' || keep.has(ch)) {\r\n cleaned += ch;\r\n }\r\n }\r\n const normalized = cleaned\r\n .split(separators.group)\r\n .join('')\r\n .replace(separators.decimal, '.');\r\n\r\n if (normalized === '' || normalized === '-' || normalized === '.' || normalized === '-.') {\r\n return null;\r\n }\r\n const num = parseFloat(normalized);\r\n return isNaN(num) ? null : num;\r\n}\r\n","// Luxon-based date parsing/formatting/serialization for nxe-date-box.\r\n//\r\n// The app sets a global DevExtreme `dateSerializationFormat: \"yyyy-MM-ddTHH:mm:ss\"`, so the value\r\n// stored in the form control is an ISO-like wall-clock string (no zone, no millis). nxe-date-box\r\n// preserves that contract: it accepts such strings (and tolerates Date / full-ISO-with-Z from the\r\n// `defaultSystemSettings` directive) and emits the same `\"yyyy-MM-ddTHH:mm:ss\"` string.\r\n\r\nimport { DateTime } from 'luxon';\r\n\r\n/** The app-wide serialization format (matches DevExtreme `dateSerializationFormat`). */\r\nexport const NXE_DATE_SERIALIZATION_FORMAT = \"yyyy-MM-dd'T'HH:mm:ss\";\r\n/** DevExtreme global default `displayFormat` (`dxDateBox.defaultOptions`). */\r\nexport const NXE_DEFAULT_DISPLAY_FORMAT = 'dd-MMM-yyyy';\r\n/** Default `displayFormat` for `type=\"datetime\"`. */\r\nexport const NXE_DEFAULT_DATETIME_DISPLAY_FORMAT = 'dd-MMM-yyyy HH:mm';\r\n/** Default `displayFormat` for `type=\"time\"`. */\r\nexport const NXE_DEFAULT_TIME_DISPLAY_FORMAT = 'HH:mm';\r\n\r\n/** Parse a bound value (wall-clock ISO string, full ISO, Date, or DateTime) into a Luxon DateTime. */\r\nexport function parseDateValue(value: unknown): DateTime | null {\r\n if (value == null || value === '') {\r\n return null;\r\n }\r\n // Passthrough rather than the null this used to return: hasTimeOfDay below is called both on\r\n // raw row values and on values a caller has already parsed, and it must answer the same either way.\r\n if (DateTime.isDateTime(value)) {\r\n return value.isValid ? value : null;\r\n }\r\n if (value instanceof Date) {\r\n const dt = DateTime.fromJSDate(value);\r\n return dt.isValid ? dt : null;\r\n }\r\n if (typeof value === 'string') {\r\n // Strip millis / trailing Z / numeric offset → treat as local wall-clock (date-preserving).\r\n const cleaned = value\r\n .replace(/\\.\\d+/, '')\r\n .replace(/Z$/i, '')\r\n .replace(/[+-]\\d{2}:?\\d{2}$/, '');\r\n const dt = DateTime.fromISO(cleaned);\r\n return dt.isValid ? dt : null;\r\n }\r\n return null;\r\n}\r\n\r\n/** Serialize a DateTime back to the `\"yyyy-MM-ddTHH:mm:ss\"` string the rest of the app expects. */\r\nexport function serializeDate(dt: DateTime): string {\r\n return dt.toFormat(NXE_DATE_SERIALIZATION_FORMAT);\r\n}\r\n\r\n/**\r\n * Whether a value carries a real time of day.\r\n *\r\n * This is the one place that states what `00:00:00` MEANS in the app's serialization format: not\r\n * \"midnight\", but \"no time was chosen\". Every date-only editor in the app writes exactly midnight,\r\n * so the calendar's all-day-vs-timed split, the grid's display format and anything else that has to\r\n * tell the two apart all read it from here and cannot drift.\r\n */\r\nexport function hasTimeOfDay(value: unknown): boolean {\r\n const dt = parseDateValue(value);\r\n return !!dt && (dt.hour !== 0 || dt.minute !== 0 || dt.second !== 0 || dt.millisecond !== 0);\r\n}\r\n\r\n/**\r\n * Whether a start/end pair describes a whole-day range.\r\n *\r\n * Deliberately \"every end that is PRESENT is midnight\", not \"start is midnight AND end is midnight\".\r\n * Half-filled pairs are a supported shape — the calendar endpoint folds `start ?? end` and treats a\r\n * row carrying only one of the two as a single-day event — and a rule that demanded both would call\r\n * such a row timed on the strength of the end that is not there.\r\n *\r\n * A pair with neither end is all-day by this reading. Degenerate, and unreachable in practice: a row\r\n * with no dates at all cannot overlap a window, so the endpoint never returns one.\r\n */\r\nexport function isAllDayRange(start: unknown, end: unknown): boolean {\r\n return !hasTimeOfDay(start) && !hasTimeOfDay(end);\r\n}\r\n\r\n/** Format a DateTime for display per `displayFormat` (DevExtreme tokens map 1:1 to Luxon). */\r\nexport function formatDisplay(dt: DateTime | null, displayFormat: string): string {\r\n if (!dt) {\r\n return '';\r\n }\r\n return dt.toFormat(displayFormat || NXE_DEFAULT_DISPLAY_FORMAT);\r\n}\r\n\r\n/**\r\n * Fixed-width tokens swapped for their variable-width twins in the lenient retry below. Luxon's\r\n * `dd` demands exactly two digits and `HH` exactly two, so `1-Aug-2026` and `9:00` are both rejected\r\n * outright by the strict pass — which is a parse failure the user has no way to diagnose.\r\n *\r\n * Month tokens are deliberately absent. Degrading `MM` to `M` mangles `MMM` — the very token every\r\n * orbit format uses — so `dd-MMM-yyyy` would become `dd-MM-yyyy` and then fail to read a\r\n * three-letter month name as a number. Numeric months stay strict; nothing in the app uses them.\r\n */\r\nconst NXE_LENIENT_TOKENS: [RegExp, string][] = [\r\n [/dd/g, 'd'],\r\n [/HH/g, 'H'],\r\n [/hh/g, 'h'],\r\n [/mm/g, 'm'],\r\n [/ss/g, 's'],\r\n];\r\n\r\nfunction lenientFormat(format: string): string {\r\n return NXE_LENIENT_TOKENS.reduce((result, [token, replacement]) => result.replace(token, replacement), format);\r\n}\r\n\r\n/**\r\n * The date-only head of a format carrying time tokens, or null when it carries none.\r\n *\r\n * Known limitation, stated rather than handled: quoted literals are not stripped before the scan, so\r\n * a format like `d 'at' HH:mm` would cut at the `a`. No orbit format uses them.\r\n */\r\nfunction dateOnlyFormat(format: string): string | null {\r\n const match = /[Hhmsa]/.exec(format);\r\n if (!match || match.index === 0) {\r\n return null;\r\n }\r\n const head = format.slice(0, match.index).replace(/[\\s,:/.\\-]+$/, '');\r\n return head || null;\r\n}\r\n\r\n/**\r\n * Parse user-typed text, trying in order: the display format; a lenient variant of it; for a format\r\n * carrying time tokens, its date-only head (keeping `base`'s time); then ISO.\r\n *\r\n * `base` is what stops a datetime field from being destructive. Without it, typing just the date\r\n * into a `dd-MMM-yyyy HH:mm` box either resets the time to midnight or — because the strict pass\r\n * rejects the text outright — nulls the whole field. Callers editing an existing value should pass\r\n * it; the parameter is optional so the date-only call sites need no change.\r\n */\r\nexport function parseDisplay(text: string, displayFormat: string, base?: DateTime | null): DateTime | null {\r\n const trimmed = (text ?? '').trim();\r\n if (!trimmed) {\r\n return null;\r\n }\r\n\r\n const format = displayFormat || NXE_DEFAULT_DISPLAY_FORMAT;\r\n const lenient = lenientFormat(format);\r\n for (const candidate of lenient === format ? [format] : [format, lenient]) {\r\n const dt = DateTime.fromFormat(trimmed, candidate);\r\n if (dt.isValid) {\r\n return dt;\r\n }\r\n }\r\n\r\n const dateOnly = dateOnlyFormat(format);\r\n if (dateOnly) {\r\n const lenientDateOnly = lenientFormat(dateOnly);\r\n for (const candidate of lenientDateOnly === dateOnly ? [dateOnly] : [dateOnly, lenientDateOnly]) {\r\n const dt = DateTime.fromFormat(trimmed, candidate);\r\n if (dt.isValid) {\r\n return base?.isValid ? dt.set({ hour: base.hour, minute: base.minute, second: base.second }) : dt;\r\n }\r\n }\r\n }\r\n\r\n const iso = DateTime.fromISO(trimmed);\r\n return iso.isValid ? iso : null;\r\n}\r\n\r\n/** Calendar zoom level (DevExtreme `dxCalendar.zoomLevel`). */\r\nexport type NxeCalendarZoom = 'month' | 'year' | 'decade' | 'century';\r\n\r\nexport interface NxePeriodCell {\r\n /** First day of the period (month / year / decade). */\r\n anchor: DateTime;\r\n label: string;\r\n /** Belongs to the adjacent decade/century (rendered dimmed, DevExtreme `other-view`). */\r\n other: boolean;\r\n}\r\n\r\n/**\r\n * Build the 4×3 period grid for a zoomed-out calendar view (mirrors DevExtreme):\r\n * `year` → Jan..Dec, `decade` → decadeStart−1..decadeStart+10, `century` → 12 decades\r\n * from centuryStart−10, labelled `\"2020 - 2029\"`.\r\n */\r\nexport function buildPeriodCells(zoom: Exclude<NxeCalendarZoom, 'month'>, view: DateTime): NxePeriodCell[] {\r\n if (zoom === 'year') {\r\n return Array.from({ length: 12 }, (_, i) => {\r\n const anchor = view.set({ month: i + 1 }).startOf('month');\r\n return { anchor, label: anchor.toFormat('MMM'), other: false };\r\n });\r\n }\r\n if (zoom === 'decade') {\r\n const start = view.year - (view.year % 10);\r\n return Array.from({ length: 12 }, (_, i) => {\r\n const year = start - 1 + i;\r\n return { anchor: DateTime.local(year, 1, 1), label: String(year), other: year < start || year > start + 9 };\r\n });\r\n }\r\n const start = view.year - (view.year % 100);\r\n return Array.from({ length: 12 }, (_, i) => {\r\n const year = start - 10 + i * 10;\r\n return { anchor: DateTime.local(year, 1, 1), label: `${year} - ${year + 9}`, other: year < start || year > start + 90 };\r\n });\r\n}\r\n\r\n/** Build the 6×7 day grid (Sunday-first) for the calendar popup of `viewMonth`. */\r\nexport function buildCalendarWeeks(viewMonth: DateTime): DateTime[][] {\r\n const firstOfMonth = viewMonth.startOf('month');\r\n const offset = firstOfMonth.weekday % 7; // luxon: Mon=1..Sun=7 → Sunday-first offset\r\n let cursor = firstOfMonth.minus({ days: offset }).startOf('day');\r\n const weeks: DateTime[][] = [];\r\n for (let w = 0; w < 6; w++) {\r\n const days: DateTime[] = [];\r\n for (let d = 0; d < 7; d++) {\r\n days.push(cursor);\r\n cursor = cursor.plus({ days: 1 });\r\n }\r\n weeks.push(days);\r\n }\r\n return weeks;\r\n}\r\n","import { InjectionToken } from '@angular/core';\r\nimport { NXE_EN_US_SEPARATORS, type NxeNumberSeparators } from './nxe-number-format';\r\n\r\nexport { NXE_EN_US_SEPARATORS, NXE_ID_SEPARATORS } from './nxe-number-format';\r\nexport type { NxeNumberSeparators } from './nxe-number-format';\r\n\r\n/**\r\n * The grouping and decimal characters every number editor renders with.\r\n *\r\n * DevExtreme solves this with a global `DevExpress.localization.locale()`; the Angular equivalent\r\n * is a provider, so an application states it once at bootstrap:\r\n *\r\n * ```ts\r\n * providers: [{ provide: NXE_NUMBER_SEPARATORS, useValue: NXE_ID_SEPARATORS }]\r\n * ```\r\n *\r\n * This exists because the alternative already happened: with the separators hardcoded, an\r\n * application that needed `1.250.000,50` instead of `1,234.50` forked the format engine to get\r\n * them, and that single file is why its copy of the library could not be merged back.\r\n *\r\n * Defaults to en-US, which is DevExtreme's default and what the pattern language itself writes.\r\n */\r\nexport const NXE_NUMBER_SEPARATORS = new InjectionToken<NxeNumberSeparators>(\r\n 'NXE_NUMBER_SEPARATORS',\r\n { providedIn: 'root', factory: () => NXE_EN_US_SEPARATORS },\r\n);\r\n","// @nexterajs/ui/intl — public API.\r\nexport * from './lib/number-box/nxe-number-format';\r\nexport * from './lib/date-box/nxe-date-format';\r\nexport * from './lib/number-box/nxe-number-locale';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nexterajs-ui-intl.mjs","sources":["../../../projects/nextera-ui/intl/src/lib/number-box/nxe-number-format.ts","../../../projects/nextera-ui/intl/src/lib/date-box/nxe-date-format.ts","../../../projects/nextera-ui/intl/src/lib/number-box/nxe-number-locale.ts","../../../projects/nextera-ui/intl/src/public-api.ts","../../../projects/nextera-ui/intl/src/nexterajs-ui-intl.ts"],"sourcesContent":["// DevExtreme LDML-style number format engine for nxe-number-box.\r\n//\r\n// Supports the patterns actually used in this codebase:\r\n// #,##0.00 — grouping, exactly 2 fraction digits\r\n// #,##0.00000 — grouping, exactly 5 fraction digits\r\n// #,##0.#####'%' — grouping, 0–5 fraction digits, literal '%' suffix (NO ×100)\r\n// #0 — integer, no grouping\r\n// #,##0 — integer with grouping\r\n// (empty) — raw number, no grouping\r\n//\r\n// Tokens: `#` optional digit, `0` required digit, `,` enable grouping (size 3), `.` decimal point\r\n// (count of `0` after it = min fraction digits, count of `0`+`#` = max), `'…'` verbatim literal.\r\n\r\nexport interface NxeParsedNumberFormat {\r\n prefix: string;\r\n suffix: string;\r\n useGrouping: boolean;\r\n /**\r\n * Digits per group, read from the pattern: `#,##0` is 3, `#,#0` is 2. Optional because\r\n * callers construct this shape by hand as well as through {@link parseNumberFormat};\r\n * omitted means dx's usual 3.\r\n */\r\n groupSize?: number;\r\n minInt: number;\r\n minFrac: number;\r\n maxFrac: number;\r\n}\r\n\r\n/** dx's cap on the significant digits a formatted number carries (ldml/number.js:21). */\r\nconst MAXIMUM_NUMBER_LENGTH = 15;\r\n\r\n/** Parse an LDML pattern into a {@link NxeParsedNumberFormat}, or `null` for raw (no format). */\r\nexport function parseNumberFormat(pattern: string | undefined | null): NxeParsedNumberFormat | null {\r\n if (!pattern) {\r\n return null;\r\n }\r\n let prefix = '';\r\n let suffix = '';\r\n let core = '';\r\n let inQuote = false;\r\n let seenCore = false;\r\n for (const ch of pattern) {\r\n if (ch === \"'\") {\r\n inQuote = !inQuote;\r\n continue;\r\n }\r\n const isToken = !inQuote && (ch === '#' || ch === '0' || ch === ',' || ch === '.');\r\n if (isToken) {\r\n core += ch;\r\n seenCore = true;\r\n } else if (!seenCore) {\r\n prefix += ch;\r\n } else {\r\n suffix += ch;\r\n }\r\n }\r\n if (!core) {\r\n return null;\r\n }\r\n const dot = core.indexOf('.');\r\n const intPart = dot === -1 ? core : core.slice(0, dot);\r\n const fracPart = dot === -1 ? '' : core.slice(dot + 1);\r\n const count = (s: string, re: RegExp) => (s.match(re) || []).length;\r\n // The grouping SIZE comes from the pattern, not from a constant 3: dx reads the digits after\r\n // the last comma, so `#,##0` groups by 3 and `#,#0` groups by 2 (which renders 1234.5 as\r\n // \"12,34.5\"). Several consumer columns use the latter, most likely by accident - but it is\r\n // what their screens show today, so parity means reproducing it.\r\n const useGrouping = intPart.includes(',');\r\n const groupSize = useGrouping\r\n ? Math.max(1, count(intPart.slice(intPart.lastIndexOf(',') + 1), /[#0]/g))\r\n : 3;\r\n return {\r\n prefix,\r\n suffix,\r\n useGrouping,\r\n groupSize,\r\n minInt: Math.max(1, count(intPart, /0/g)),\r\n minFrac: count(fracPart, /0/g),\r\n maxFrac: count(fracPart, /[#0]/g),\r\n };\r\n}\r\n\r\n/**\r\n * Insert `sep` every `size` digits from the right.\r\n *\r\n * Written as a loop rather than a dynamic RegExp on purpose: the group size varies with the\r\n * pattern, and building a regex by interpolating into a template literal puts `\\B` and `\\d` one\r\n * missing backslash away from silently matching nothing.\r\n */\r\nfunction groupDigits(digits: string, size: number, sep: string): string {\r\n let out = '';\r\n for (let i = 0; i < digits.length; i++) {\r\n if (i > 0 && (digits.length - i) % size === 0) {\r\n out += sep;\r\n }\r\n out += digits[i];\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Render `value` with exactly `frac` fraction digits, from its SHORTEST decimal form.\r\n *\r\n * `toFixed` expands the binary value instead, so 1234567.891 at 14 digits becomes\r\n * \"1234567.89100000006147\" — the true double, and not what dx prints. Taking `String(value)`\r\n * (the shortest round-tripping decimal) and padding matches dx and is what a reader expects.\r\n * Exponential notation, which `String` produces below 1e-6, falls back to `toFixed`.\r\n */\r\nfunction toDecimalString(value: number, frac: number): string {\r\n const s = String(value);\r\n if (s.includes('e') || s.includes('E')) {\r\n return value.toFixed(frac);\r\n }\r\n const dot = s.indexOf('.');\r\n const intPart = dot === -1 ? s : s.slice(0, dot);\r\n const fracPart = dot === -1 ? '' : s.slice(dot + 1);\r\n return frac === 0 ? intPart : `${intPart}.${fracPart.slice(0, frac).padEnd(frac, '0')}`;\r\n}\r\n\r\n/** Round half-away-from-zero to `frac` fraction digits (DevExtreme rounding), fp-safe. */\r\nexport function roundHalfAwayFromZero(value: number, frac: number): number {\r\n const factor = Math.pow(10, frac);\r\n const rounded = Math.round(Math.abs(value) * factor + 1e-9) / factor;\r\n return value < 0 ? -rounded : rounded;\r\n}\r\n\r\n/** Format a number with the parsed pattern. `null`/`NaN` → `''`. */\r\n/**\r\n * The characters that separate groups and the fraction.\r\n *\r\n * The LDML pattern always writes `,` for grouping and `.` for the decimal point — that is the\r\n * pattern language, not the output. Which characters actually get rendered is a locale decision,\r\n * and it is the one that split this file: one consuming app needs `1,234.50`, another needs\r\n * `1.250.000,50`, and hardcoding either forces the other to fork.\r\n */\r\nexport interface NxeNumberSeparators {\r\n readonly group: string;\r\n readonly decimal: string;\r\n}\r\n\r\n/** DevExtreme's default, and this library's. */\r\nexport const NXE_EN_US_SEPARATORS: NxeNumberSeparators = { group: ',', decimal: '.' };\r\n\r\n/** Indonesian house style, used by several of the consuming applications. */\r\nexport const NXE_ID_SEPARATORS: NxeNumberSeparators = { group: '.', decimal: ',' };\r\n\r\nexport function formatNumber(\r\n value: number | null | undefined,\r\n fmt: NxeParsedNumberFormat | null,\r\n separators: NxeNumberSeparators = NXE_EN_US_SEPARATORS,\r\n): string {\r\n if (value == null || typeof value !== 'number' || isNaN(value)) {\r\n return '';\r\n }\r\n if (!fmt) {\r\n return String(value);\r\n }\r\n const negative = value < 0;\r\n const abs = Math.abs(value);\r\n // dx MAXIMUM_NUMBER_LENGTH (ldml/number.js:21, :152-153): the fraction gets only what is left\r\n // of 15 significant digits after the integer part, `fitIntoRange(maxFloat, 0, 15 - intLen)`.\r\n // Without it a 15-place mask printed 10/3 % as 3.333333333333333 rather than dx's\r\n // 3.33333333333333.\r\n const frac = Math.min(fmt.maxFrac, Math.max(0, MAXIMUM_NUMBER_LENGTH - String(Math.floor(abs)).length));\r\n const rounded = roundHalfAwayFromZero(abs, frac);\r\n const fixed = toDecimalString(rounded, frac);\r\n const parts = fixed.split('.');\r\n let intStr = parts[0];\r\n let fracStr = parts[1] ?? '';\r\n if (fmt.maxFrac > fmt.minFrac) {\r\n fracStr = fracStr.replace(/0+$/, '');\r\n }\r\n // Always, not only for optional digits: the cap can leave fewer places than the mask requires,\r\n // and dx's normalizeValueString pads those back with zeros.\r\n while (fracStr.length < fmt.minFrac) {\r\n fracStr += '0';\r\n }\r\n while (intStr.length < fmt.minInt) {\r\n intStr = '0' + intStr;\r\n }\r\n if (fmt.useGrouping) {\r\n intStr = groupDigits(intStr, fmt.groupSize ?? 3, separators.group);\r\n }\r\n const body = fracStr ? `${intStr}${separators.decimal}${fracStr}` : intStr;\r\n return `${fmt.prefix}${negative ? '-' : ''}${body}${fmt.suffix}`;\r\n}\r\n\r\n/** Parse a (possibly formatted) string into a number; strips grouping/literals. Empty → `null`. */\r\nexport function parseNumber(\r\n text: string | null | undefined,\r\n separators: NxeNumberSeparators = NXE_EN_US_SEPARATORS,\r\n): number | null {\r\n if (text == null) {\r\n return null;\r\n }\r\n // Strip everything that is not a digit, a sign, or one of this locale's separators, then\r\n // drop the group separator and normalise the decimal one to `.` for parseFloat.\r\n const keep = new Set([separators.group, separators.decimal]);\r\n let cleaned = '';\r\n for (const ch of text) {\r\n if ((ch >= '0' && ch <= '9') || ch === '-' || keep.has(ch)) {\r\n cleaned += ch;\r\n }\r\n }\r\n const normalized = cleaned\r\n .split(separators.group)\r\n .join('')\r\n .replace(separators.decimal, '.');\r\n\r\n if (normalized === '' || normalized === '-' || normalized === '.' || normalized === '-.') {\r\n return null;\r\n }\r\n const num = parseFloat(normalized);\r\n return isNaN(num) ? null : num;\r\n}\r\n","// Luxon-based date parsing/formatting/serialization for nxe-date-box.\r\n//\r\n// The app sets a global DevExtreme `dateSerializationFormat: \"yyyy-MM-ddTHH:mm:ss\"`, so the value\r\n// stored in the form control is an ISO-like wall-clock string (no zone, no millis). nxe-date-box\r\n// preserves that contract: it accepts such strings (and tolerates Date / full-ISO-with-Z from the\r\n// `defaultSystemSettings` directive) and emits the same `\"yyyy-MM-ddTHH:mm:ss\"` string.\r\n\r\nimport { DateTime } from 'luxon';\r\n\r\n/** The app-wide serialization format (matches DevExtreme `dateSerializationFormat`). */\r\nexport const NXE_DATE_SERIALIZATION_FORMAT = \"yyyy-MM-dd'T'HH:mm:ss\";\r\n/** DevExtreme global default `displayFormat` (`dxDateBox.defaultOptions`). */\r\nexport const NXE_DEFAULT_DISPLAY_FORMAT = 'dd-MMM-yyyy';\r\n/** Default `displayFormat` for `type=\"datetime\"`. */\r\nexport const NXE_DEFAULT_DATETIME_DISPLAY_FORMAT = 'dd-MMM-yyyy HH:mm';\r\n/** Default `displayFormat` for `type=\"time\"`. */\r\nexport const NXE_DEFAULT_TIME_DISPLAY_FORMAT = 'HH:mm';\r\n\r\n/** Parse a bound value (wall-clock ISO string, full ISO, Date, or DateTime) into a Luxon DateTime. */\r\nexport function parseDateValue(value: unknown): DateTime | null {\r\n if (value == null || value === '') {\r\n return null;\r\n }\r\n // Passthrough rather than the null this used to return: hasTimeOfDay below is called both on\r\n // raw row values and on values a caller has already parsed, and it must answer the same either way.\r\n if (DateTime.isDateTime(value)) {\r\n return value.isValid ? value : null;\r\n }\r\n if (value instanceof Date) {\r\n const dt = DateTime.fromJSDate(value);\r\n return dt.isValid ? dt : null;\r\n }\r\n if (typeof value === 'string') {\r\n // Strip millis / trailing Z / numeric offset → treat as local wall-clock (date-preserving).\r\n const cleaned = value\r\n .replace(/\\.\\d+/, '')\r\n .replace(/Z$/i, '')\r\n .replace(/[+-]\\d{2}:?\\d{2}$/, '');\r\n const dt = DateTime.fromISO(cleaned);\r\n return dt.isValid ? dt : null;\r\n }\r\n return null;\r\n}\r\n\r\n/** Serialize a DateTime back to the `\"yyyy-MM-ddTHH:mm:ss\"` string the rest of the app expects. */\r\nexport function serializeDate(dt: DateTime): string {\r\n return dt.toFormat(NXE_DATE_SERIALIZATION_FORMAT);\r\n}\r\n\r\n/**\r\n * Whether a value carries a real time of day.\r\n *\r\n * This is the one place that states what `00:00:00` MEANS in the app's serialization format: not\r\n * \"midnight\", but \"no time was chosen\". Every date-only editor in the app writes exactly midnight,\r\n * so the calendar's all-day-vs-timed split, the grid's display format and anything else that has to\r\n * tell the two apart all read it from here and cannot drift.\r\n */\r\nexport function hasTimeOfDay(value: unknown): boolean {\r\n const dt = parseDateValue(value);\r\n return !!dt && (dt.hour !== 0 || dt.minute !== 0 || dt.second !== 0 || dt.millisecond !== 0);\r\n}\r\n\r\n/**\r\n * Whether a start/end pair describes a whole-day range.\r\n *\r\n * Deliberately \"every end that is PRESENT is midnight\", not \"start is midnight AND end is midnight\".\r\n * Half-filled pairs are a supported shape — the calendar endpoint folds `start ?? end` and treats a\r\n * row carrying only one of the two as a single-day event — and a rule that demanded both would call\r\n * such a row timed on the strength of the end that is not there.\r\n *\r\n * A pair with neither end is all-day by this reading. Degenerate, and unreachable in practice: a row\r\n * with no dates at all cannot overlap a window, so the endpoint never returns one.\r\n */\r\nexport function isAllDayRange(start: unknown, end: unknown): boolean {\r\n return !hasTimeOfDay(start) && !hasTimeOfDay(end);\r\n}\r\n\r\n/** Format a DateTime for display per `displayFormat` (DevExtreme tokens map 1:1 to Luxon). */\r\nexport function formatDisplay(dt: DateTime | null, displayFormat: string): string {\r\n if (!dt) {\r\n return '';\r\n }\r\n return dt.toFormat(displayFormat || NXE_DEFAULT_DISPLAY_FORMAT);\r\n}\r\n\r\n/**\r\n * Fixed-width tokens swapped for their variable-width twins in the lenient retry below. Luxon's\r\n * `dd` demands exactly two digits and `HH` exactly two, so `1-Aug-2026` and `9:00` are both rejected\r\n * outright by the strict pass — which is a parse failure the user has no way to diagnose.\r\n *\r\n * Month tokens are deliberately absent. Degrading `MM` to `M` mangles `MMM` — the very token every\r\n * orbit format uses — so `dd-MMM-yyyy` would become `dd-MM-yyyy` and then fail to read a\r\n * three-letter month name as a number. Numeric months stay strict; nothing in the app uses them.\r\n */\r\nconst NXE_LENIENT_TOKENS: [RegExp, string][] = [\r\n [/dd/g, 'd'],\r\n [/HH/g, 'H'],\r\n [/hh/g, 'h'],\r\n [/mm/g, 'm'],\r\n [/ss/g, 's'],\r\n];\r\n\r\nfunction lenientFormat(format: string): string {\r\n return NXE_LENIENT_TOKENS.reduce((result, [token, replacement]) => result.replace(token, replacement), format);\r\n}\r\n\r\n/**\r\n * The date-only head of a format carrying time tokens, or null when it carries none.\r\n *\r\n * Known limitation, stated rather than handled: quoted literals are not stripped before the scan, so\r\n * a format like `d 'at' HH:mm` would cut at the `a`. No orbit format uses them.\r\n */\r\nfunction dateOnlyFormat(format: string): string | null {\r\n const match = /[Hhmsa]/.exec(format);\r\n if (!match || match.index === 0) {\r\n return null;\r\n }\r\n const head = format.slice(0, match.index).replace(/[\\s,:/.\\-]+$/, '');\r\n return head || null;\r\n}\r\n\r\n/**\r\n * Parse user-typed text, trying in order: the display format; a lenient variant of it; for a format\r\n * carrying time tokens, its date-only head (keeping `base`'s time); then ISO.\r\n *\r\n * `base` is what stops a datetime field from being destructive. Without it, typing just the date\r\n * into a `dd-MMM-yyyy HH:mm` box either resets the time to midnight or — because the strict pass\r\n * rejects the text outright — nulls the whole field. Callers editing an existing value should pass\r\n * it; the parameter is optional so the date-only call sites need no change.\r\n */\r\nexport function parseDisplay(text: string, displayFormat: string, base?: DateTime | null): DateTime | null {\r\n const trimmed = (text ?? '').trim();\r\n if (!trimmed) {\r\n return null;\r\n }\r\n\r\n const format = displayFormat || NXE_DEFAULT_DISPLAY_FORMAT;\r\n const lenient = lenientFormat(format);\r\n for (const candidate of lenient === format ? [format] : [format, lenient]) {\r\n const dt = DateTime.fromFormat(trimmed, candidate);\r\n if (dt.isValid) {\r\n return dt;\r\n }\r\n }\r\n\r\n const dateOnly = dateOnlyFormat(format);\r\n if (dateOnly) {\r\n const lenientDateOnly = lenientFormat(dateOnly);\r\n for (const candidate of lenientDateOnly === dateOnly ? [dateOnly] : [dateOnly, lenientDateOnly]) {\r\n const dt = DateTime.fromFormat(trimmed, candidate);\r\n if (dt.isValid) {\r\n return base?.isValid ? dt.set({ hour: base.hour, minute: base.minute, second: base.second }) : dt;\r\n }\r\n }\r\n }\r\n\r\n const iso = DateTime.fromISO(trimmed);\r\n return iso.isValid ? iso : null;\r\n}\r\n\r\n/** Calendar zoom level (DevExtreme `dxCalendar.zoomLevel`). */\r\nexport type NxeCalendarZoom = 'month' | 'year' | 'decade' | 'century';\r\n\r\nexport interface NxePeriodCell {\r\n /** First day of the period (month / year / decade). */\r\n anchor: DateTime;\r\n label: string;\r\n /** Belongs to the adjacent decade/century (rendered dimmed, DevExtreme `other-view`). */\r\n other: boolean;\r\n}\r\n\r\n/**\r\n * Build the 4×3 period grid for a zoomed-out calendar view (mirrors DevExtreme):\r\n * `year` → Jan..Dec, `decade` → decadeStart−1..decadeStart+10, `century` → 12 decades\r\n * from centuryStart−10, labelled `\"2020 - 2029\"`.\r\n */\r\nexport function buildPeriodCells(zoom: Exclude<NxeCalendarZoom, 'month'>, view: DateTime): NxePeriodCell[] {\r\n if (zoom === 'year') {\r\n return Array.from({ length: 12 }, (_, i) => {\r\n const anchor = view.set({ month: i + 1 }).startOf('month');\r\n return { anchor, label: anchor.toFormat('MMM'), other: false };\r\n });\r\n }\r\n if (zoom === 'decade') {\r\n const start = view.year - (view.year % 10);\r\n return Array.from({ length: 12 }, (_, i) => {\r\n const year = start - 1 + i;\r\n return { anchor: DateTime.local(year, 1, 1), label: String(year), other: year < start || year > start + 9 };\r\n });\r\n }\r\n const start = view.year - (view.year % 100);\r\n return Array.from({ length: 12 }, (_, i) => {\r\n const year = start - 10 + i * 10;\r\n return { anchor: DateTime.local(year, 1, 1), label: `${year} - ${year + 9}`, other: year < start || year > start + 90 };\r\n });\r\n}\r\n\r\n/** Build the 6×7 day grid (Sunday-first) for the calendar popup of `viewMonth`. */\r\nexport function buildCalendarWeeks(viewMonth: DateTime): DateTime[][] {\r\n const firstOfMonth = viewMonth.startOf('month');\r\n const offset = firstOfMonth.weekday % 7; // luxon: Mon=1..Sun=7 → Sunday-first offset\r\n let cursor = firstOfMonth.minus({ days: offset }).startOf('day');\r\n const weeks: DateTime[][] = [];\r\n for (let w = 0; w < 6; w++) {\r\n const days: DateTime[] = [];\r\n for (let d = 0; d < 7; d++) {\r\n days.push(cursor);\r\n cursor = cursor.plus({ days: 1 });\r\n }\r\n weeks.push(days);\r\n }\r\n return weeks;\r\n}\r\n","import { InjectionToken } from '@angular/core';\r\nimport { NXE_EN_US_SEPARATORS, type NxeNumberSeparators } from './nxe-number-format';\r\n\r\nexport { NXE_EN_US_SEPARATORS, NXE_ID_SEPARATORS } from './nxe-number-format';\r\nexport type { NxeNumberSeparators } from './nxe-number-format';\r\n\r\n/**\r\n * The grouping and decimal characters every number editor renders with.\r\n *\r\n * DevExtreme solves this with a global `DevExpress.localization.locale()`; the Angular equivalent\r\n * is a provider, so an application states it once at bootstrap:\r\n *\r\n * ```ts\r\n * providers: [{ provide: NXE_NUMBER_SEPARATORS, useValue: NXE_ID_SEPARATORS }]\r\n * ```\r\n *\r\n * This exists because the alternative already happened: with the separators hardcoded, an\r\n * application that needed `1.250.000,50` instead of `1,234.50` forked the format engine to get\r\n * them, and that single file is why its copy of the library could not be merged back.\r\n *\r\n * Defaults to en-US, which is DevExtreme's default and what the pattern language itself writes.\r\n */\r\nexport const NXE_NUMBER_SEPARATORS = new InjectionToken<NxeNumberSeparators>(\r\n 'NXE_NUMBER_SEPARATORS',\r\n { providedIn: 'root', factory: () => NXE_EN_US_SEPARATORS },\r\n);\r\n","// @nexterajs/ui/intl — public API.\r\nexport * from './lib/number-box/nxe-number-format';\r\nexport * from './lib/date-box/nxe-date-format';\r\nexport * from './lib/number-box/nxe-number-locale';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, input, output, signal, computed, SecurityContext, ChangeDetectionStrategy, Component, makeEnvironmentProviders, ApplicationRef, EnvironmentInjector, createComponent, Injectable } from '@angular/core';
|
|
3
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
4
|
+
import { DOCUMENT } from '@angular/common';
|
|
5
|
+
|
|
6
|
+
const NXE_TOAST_DEFAULTS = {
|
|
7
|
+
positionClass: 'toast-top-right',
|
|
8
|
+
timeOut: 5000,
|
|
9
|
+
closeButton: false,
|
|
10
|
+
progressBar: false,
|
|
11
|
+
tapToDismiss: true,
|
|
12
|
+
enableHtml: false,
|
|
13
|
+
newestOnTop: true,
|
|
14
|
+
preventDuplicates: false,
|
|
15
|
+
maxOpened: 0,
|
|
16
|
+
};
|
|
17
|
+
const NXE_TOAST_CONFIG = new InjectionToken('NXE_TOAST_CONFIG', {
|
|
18
|
+
providedIn: 'root',
|
|
19
|
+
factory: () => ({}),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* One toast. Rendered by {@link NxeToastService} into its container; not used directly.
|
|
24
|
+
*
|
|
25
|
+
* **The element order is load-bearing.** The close button comes first and is floated right, which
|
|
26
|
+
* is how the library this replaces positioned it — move it after the title and it drops below the
|
|
27
|
+
* text instead of sitting in the corner. Likewise the host carries the classes rather than an
|
|
28
|
+
* inner wrapper, because the shipped stylesheet targets `.nxe-toast-container .nxe-toast`
|
|
29
|
+
* directly and an extra element between them changes what `width` and `margin` apply to.
|
|
30
|
+
*
|
|
31
|
+
* Styles are global (`@nexterajs/ui/styles/_toast.scss`), not encapsulated, so an application's
|
|
32
|
+
* existing overrides keep winning on specificity exactly as they did before.
|
|
33
|
+
*
|
|
34
|
+
* Accessibility, which the original had none of:
|
|
35
|
+
* - `error` and `warning` announce as `alert` (assertive — the user's own action caused it).
|
|
36
|
+
* `success` and `info` announce as `status` (polite).
|
|
37
|
+
* - Focus is never taken. A validation toast typically appears mid-keystroke, and moving focus
|
|
38
|
+
* would pull the caret out of the field being corrected.
|
|
39
|
+
* - The close button is a real `<button>` with a label, reachable by keyboard.
|
|
40
|
+
*/
|
|
41
|
+
class NxeToastComponent {
|
|
42
|
+
sanitizer = inject(DomSanitizer);
|
|
43
|
+
type = input.required(/* @ts-ignore */
|
|
44
|
+
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
45
|
+
title = input(null, /* @ts-ignore */
|
|
46
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
47
|
+
message = input(null, /* @ts-ignore */
|
|
48
|
+
...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
|
|
49
|
+
enableHtml = input(false, /* @ts-ignore */
|
|
50
|
+
...(ngDevMode ? [{ debugName: "enableHtml" }] : /* istanbul ignore next */ []));
|
|
51
|
+
tapToDismiss = input(true, /* @ts-ignore */
|
|
52
|
+
...(ngDevMode ? [{ debugName: "tapToDismiss" }] : /* istanbul ignore next */ []));
|
|
53
|
+
closeButton = input(false, /* @ts-ignore */
|
|
54
|
+
...(ngDevMode ? [{ debugName: "closeButton" }] : /* istanbul ignore next */ []));
|
|
55
|
+
progressBar = input(false, /* @ts-ignore */
|
|
56
|
+
...(ngDevMode ? [{ debugName: "progressBar" }] : /* istanbul ignore next */ []));
|
|
57
|
+
toastClass = input('', /* @ts-ignore */
|
|
58
|
+
...(ngDevMode ? [{ debugName: "toastClass" }] : /* istanbul ignore next */ []));
|
|
59
|
+
titleClass = input('', /* @ts-ignore */
|
|
60
|
+
...(ngDevMode ? [{ debugName: "titleClass" }] : /* istanbul ignore next */ []));
|
|
61
|
+
messageClass = input('', /* @ts-ignore */
|
|
62
|
+
...(ngDevMode ? [{ debugName: "messageClass" }] : /* istanbul ignore next */ []));
|
|
63
|
+
dismissed = output();
|
|
64
|
+
/** Pre-enter state: the service clears it a frame later so the opacity transition runs. */
|
|
65
|
+
entering = signal(true, /* @ts-ignore */
|
|
66
|
+
...(ngDevMode ? [{ debugName: "entering" }] : /* istanbul ignore next */ []));
|
|
67
|
+
/** Set while the leave transition plays; the service removes the view once it finishes. */
|
|
68
|
+
dismissing = signal(false, /* @ts-ignore */
|
|
69
|
+
...(ngDevMode ? [{ debugName: "dismissing" }] : /* istanbul ignore next */ []));
|
|
70
|
+
progressWidth = signal(100, /* @ts-ignore */
|
|
71
|
+
...(ngDevMode ? [{ debugName: "progressWidth" }] : /* istanbul ignore next */ []));
|
|
72
|
+
assertive = computed(() => this.type() === 'error' || this.type() === 'warning', /* @ts-ignore */
|
|
73
|
+
...(ngDevMode ? [{ debugName: "assertive" }] : /* istanbul ignore next */ []));
|
|
74
|
+
hostClass = computed(() => [
|
|
75
|
+
'nxe-toast',
|
|
76
|
+
`nxe-toast-${this.type()}`,
|
|
77
|
+
this.toastClass(),
|
|
78
|
+
this.entering() ? 'nxe-toast-entering' : '',
|
|
79
|
+
this.dismissing() ? 'nxe-toast-leaving' : '',
|
|
80
|
+
]
|
|
81
|
+
.filter(Boolean)
|
|
82
|
+
.join(' '), /* @ts-ignore */
|
|
83
|
+
...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
|
|
84
|
+
/**
|
|
85
|
+
* Sanitised HTML for the message.
|
|
86
|
+
*
|
|
87
|
+
* Callers pass server-composed strings here — validation failures joined with `<br>` — so this
|
|
88
|
+
* is data, not markup the application authored. `sanitize` drops anything executable and keeps
|
|
89
|
+
* the line breaks the lists depend on.
|
|
90
|
+
*/
|
|
91
|
+
safeMessage = computed(() => this.sanitizer.sanitize(SecurityContext.HTML, this.message() ?? '') ?? '', /* @ts-ignore */
|
|
92
|
+
...(ngDevMode ? [{ debugName: "safeMessage" }] : /* istanbul ignore next */ []));
|
|
93
|
+
onBodyClick() {
|
|
94
|
+
if (this.tapToDismiss())
|
|
95
|
+
this.dismissed.emit();
|
|
96
|
+
}
|
|
97
|
+
onCloseClick(event) {
|
|
98
|
+
// Without this the host click handler also fires, dismissing twice.
|
|
99
|
+
event.stopPropagation();
|
|
100
|
+
this.dismissed.emit();
|
|
101
|
+
}
|
|
102
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
103
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeToastComponent, isStandalone: true, selector: "nxe-toast", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, enableHtml: { classPropertyName: "enableHtml", publicName: "enableHtml", isSignal: true, isRequired: false, transformFunction: null }, tapToDismiss: { classPropertyName: "tapToDismiss", publicName: "tapToDismiss", isSignal: true, isRequired: false, transformFunction: null }, closeButton: { classPropertyName: "closeButton", publicName: "closeButton", isSignal: true, isRequired: false, transformFunction: null }, progressBar: { classPropertyName: "progressBar", publicName: "progressBar", isSignal: true, isRequired: false, transformFunction: null }, toastClass: { classPropertyName: "toastClass", publicName: "toastClass", isSignal: true, isRequired: false, transformFunction: null }, titleClass: { classPropertyName: "titleClass", publicName: "titleClass", isSignal: true, isRequired: false, transformFunction: null }, messageClass: { classPropertyName: "messageClass", publicName: "messageClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismissed: "dismissed" }, host: { attributes: { "aria-atomic": "true" }, listeners: { "click": "onBodyClick()" }, properties: { "class": "hostClass()", "attr.role": "assertive() ? \"alert\" : \"status\"", "attr.aria-live": "assertive() ? \"assertive\" : \"polite\"" } }, ngImport: i0, template: `
|
|
104
|
+
@if (closeButton()) {
|
|
105
|
+
<button type="button" class="nxe-toast-close" aria-label="Close notification" (click)="onCloseClick($event)">
|
|
106
|
+
×
|
|
107
|
+
</button>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@if (title()) {
|
|
111
|
+
<div [class]="'nxe-toast-title ' + titleClass()" [attr.aria-label]="title()">{{ title() }}</div>
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@if (message()) {
|
|
115
|
+
@if (enableHtml()) {
|
|
116
|
+
<div [class]="'nxe-toast-message ' + messageClass()" [innerHTML]="safeMessage()"></div>
|
|
117
|
+
} @else {
|
|
118
|
+
<div [class]="'nxe-toast-message ' + messageClass()">{{ message() }}</div>
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@if (progressBar()) {
|
|
123
|
+
<div><div class="nxe-toast-progress" [style.width.%]="progressWidth()"></div></div>
|
|
124
|
+
}
|
|
125
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
126
|
+
}
|
|
127
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeToastComponent, decorators: [{
|
|
128
|
+
type: Component,
|
|
129
|
+
args: [{
|
|
130
|
+
selector: 'nxe-toast',
|
|
131
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
132
|
+
host: {
|
|
133
|
+
'[class]': 'hostClass()',
|
|
134
|
+
'[attr.role]': 'assertive() ? "alert" : "status"',
|
|
135
|
+
'[attr.aria-live]': 'assertive() ? "assertive" : "polite"',
|
|
136
|
+
'aria-atomic': 'true',
|
|
137
|
+
'(click)': 'onBodyClick()',
|
|
138
|
+
},
|
|
139
|
+
template: `
|
|
140
|
+
@if (closeButton()) {
|
|
141
|
+
<button type="button" class="nxe-toast-close" aria-label="Close notification" (click)="onCloseClick($event)">
|
|
142
|
+
×
|
|
143
|
+
</button>
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@if (title()) {
|
|
147
|
+
<div [class]="'nxe-toast-title ' + titleClass()" [attr.aria-label]="title()">{{ title() }}</div>
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@if (message()) {
|
|
151
|
+
@if (enableHtml()) {
|
|
152
|
+
<div [class]="'nxe-toast-message ' + messageClass()" [innerHTML]="safeMessage()"></div>
|
|
153
|
+
} @else {
|
|
154
|
+
<div [class]="'nxe-toast-message ' + messageClass()">{{ message() }}</div>
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@if (progressBar()) {
|
|
159
|
+
<div><div class="nxe-toast-progress" [style.width.%]="progressWidth()"></div></div>
|
|
160
|
+
}
|
|
161
|
+
`,
|
|
162
|
+
}]
|
|
163
|
+
}], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], enableHtml: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableHtml", required: false }] }], tapToDismiss: [{ type: i0.Input, args: [{ isSignal: true, alias: "tapToDismiss", required: false }] }], closeButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeButton", required: false }] }], progressBar: [{ type: i0.Input, args: [{ isSignal: true, alias: "progressBar", required: false }] }], toastClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "toastClass", required: false }] }], titleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleClass", required: false }] }], messageClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "messageClass", required: false }] }], dismissed: [{ type: i0.Output, args: ["dismissed"] }] } });
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Configure the toast stack once, at bootstrap.
|
|
167
|
+
*
|
|
168
|
+
* ```ts
|
|
169
|
+
* providers: [provideNxeToast({ positionClass: 'toast-top-center' })]
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
function provideNxeToast(config = {}) {
|
|
173
|
+
return makeEnvironmentProviders([{ provide: NXE_TOAST_CONFIG, useValue: config }]);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Shows toasts.
|
|
177
|
+
*
|
|
178
|
+
* **The container is deliberately not a CDK overlay.** A CDK overlay pane lands in the same
|
|
179
|
+
* `.cdk-overlay-container` as the application's dialogs, which makes paint order creation order —
|
|
180
|
+
* so a toast raised right after a dialog closes can end up behind the next dialog that opens.
|
|
181
|
+
* Callers do exactly that. A dedicated container with its own stacking context keeps toasts above
|
|
182
|
+
* dialogs regardless of the order things were created in.
|
|
183
|
+
*/
|
|
184
|
+
class NxeToastService {
|
|
185
|
+
appRef = inject(ApplicationRef);
|
|
186
|
+
injector = inject(EnvironmentInjector);
|
|
187
|
+
document = inject(DOCUMENT);
|
|
188
|
+
userConfig = inject(NXE_TOAST_CONFIG);
|
|
189
|
+
config = { ...NXE_TOAST_DEFAULTS, ...this.userConfig };
|
|
190
|
+
container = null;
|
|
191
|
+
active = [];
|
|
192
|
+
success(message, title, override) {
|
|
193
|
+
return this.show('success', message, title, override);
|
|
194
|
+
}
|
|
195
|
+
error(message, title, override) {
|
|
196
|
+
return this.show('error', message, title, override);
|
|
197
|
+
}
|
|
198
|
+
warning(message, title, override) {
|
|
199
|
+
return this.show('warning', message, title, override);
|
|
200
|
+
}
|
|
201
|
+
info(message, title, override) {
|
|
202
|
+
return this.show('info', message, title, override);
|
|
203
|
+
}
|
|
204
|
+
/** Dismiss everything currently showing. */
|
|
205
|
+
clear() {
|
|
206
|
+
for (const toast of [...this.active])
|
|
207
|
+
this.remove(toast);
|
|
208
|
+
}
|
|
209
|
+
show(type, message, title, override) {
|
|
210
|
+
const options = { ...this.config, ...(override ?? {}) };
|
|
211
|
+
const key = `${type}|${title ?? ''}|${message ?? ''}`;
|
|
212
|
+
if (options.preventDuplicates) {
|
|
213
|
+
const existing = this.active.find((toast) => toast.key === key);
|
|
214
|
+
if (existing)
|
|
215
|
+
return existing;
|
|
216
|
+
}
|
|
217
|
+
// Trim the oldest first: the newest toast is the one the user's last action produced.
|
|
218
|
+
if (options.maxOpened > 0) {
|
|
219
|
+
while (this.active.length >= options.maxOpened)
|
|
220
|
+
this.remove(this.active[0]);
|
|
221
|
+
}
|
|
222
|
+
const componentRef = createComponent(NxeToastComponent, {
|
|
223
|
+
environmentInjector: this.injector,
|
|
224
|
+
});
|
|
225
|
+
componentRef.setInput('type', type);
|
|
226
|
+
componentRef.setInput('title', title ?? null);
|
|
227
|
+
componentRef.setInput('message', message ?? null);
|
|
228
|
+
componentRef.setInput('enableHtml', options.enableHtml);
|
|
229
|
+
componentRef.setInput('tapToDismiss', options.tapToDismiss);
|
|
230
|
+
componentRef.setInput('closeButton', options.closeButton);
|
|
231
|
+
componentRef.setInput('progressBar', options.progressBar);
|
|
232
|
+
componentRef.setInput('toastClass', options.toastClass ?? '');
|
|
233
|
+
componentRef.setInput('titleClass', options.titleClass ?? '');
|
|
234
|
+
componentRef.setInput('messageClass', options.messageClass ?? '');
|
|
235
|
+
const toast = {
|
|
236
|
+
componentRef,
|
|
237
|
+
timer: null,
|
|
238
|
+
key,
|
|
239
|
+
type,
|
|
240
|
+
title: title ?? null,
|
|
241
|
+
message: message ?? null,
|
|
242
|
+
dismiss: () => this.remove(toast),
|
|
243
|
+
};
|
|
244
|
+
componentRef.instance.dismissed.subscribe(() => this.remove(toast));
|
|
245
|
+
const host = this.ensureContainer(options.positionClass);
|
|
246
|
+
// `newestOnTop` is prepend-vs-append, and getting it backwards is invisible to every test
|
|
247
|
+
// while being immediately obvious to anyone who has used the application.
|
|
248
|
+
if (options.newestOnTop)
|
|
249
|
+
host.insertBefore(componentRef.location.nativeElement, host.firstChild);
|
|
250
|
+
else
|
|
251
|
+
host.appendChild(componentRef.location.nativeElement);
|
|
252
|
+
this.appRef.attachView(componentRef.hostView);
|
|
253
|
+
// This view is created imperatively rather than by a template, so nothing else will render it
|
|
254
|
+
// before the next scheduled tick. Render it now: a toast that appears a frame late is a
|
|
255
|
+
// flicker, and callers legitimately read the DOM straight after raising one.
|
|
256
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
257
|
+
this.active.push(toast);
|
|
258
|
+
// The toast mounts at opacity 0 and transitions in. Clearing the flag in a later frame is what
|
|
259
|
+
// gives the browser a starting value to animate from — set it in the same frame and there is
|
|
260
|
+
// no transition at all, just an instant appearance.
|
|
261
|
+
requestAnimationFrame(() => {
|
|
262
|
+
if (!componentRef.hostView.destroyed) {
|
|
263
|
+
componentRef.instance.entering.set(false);
|
|
264
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
if (options.timeOut > 0) {
|
|
268
|
+
toast.timer = setTimeout(() => this.remove(toast), options.timeOut);
|
|
269
|
+
if (options.progressBar)
|
|
270
|
+
this.runProgress(toast, options.timeOut);
|
|
271
|
+
}
|
|
272
|
+
return toast;
|
|
273
|
+
}
|
|
274
|
+
/** Shrink the progress bar across the toast's lifetime, the way the original drove it. */
|
|
275
|
+
runProgress(toast, timeOut) {
|
|
276
|
+
const started = performance.now();
|
|
277
|
+
const tick = (now) => {
|
|
278
|
+
if (toast.componentRef.hostView.destroyed)
|
|
279
|
+
return;
|
|
280
|
+
const remaining = Math.max(0, 100 - ((now - started) / timeOut) * 100);
|
|
281
|
+
toast.componentRef.instance.progressWidth.set(remaining);
|
|
282
|
+
toast.componentRef.changeDetectorRef.detectChanges();
|
|
283
|
+
if (remaining > 0)
|
|
284
|
+
requestAnimationFrame(tick);
|
|
285
|
+
};
|
|
286
|
+
requestAnimationFrame(tick);
|
|
287
|
+
}
|
|
288
|
+
remove(toast) {
|
|
289
|
+
const index = this.active.indexOf(toast);
|
|
290
|
+
if (index === -1)
|
|
291
|
+
return;
|
|
292
|
+
this.active.splice(index, 1);
|
|
293
|
+
if (toast.timer !== null)
|
|
294
|
+
clearTimeout(toast.timer);
|
|
295
|
+
toast.componentRef.instance.dismissing.set(true);
|
|
296
|
+
// Flush the leave class now rather than on the next tick, so the animation starts in the same
|
|
297
|
+
// frame as the dismissal and the `animationend` below is guaranteed to have something to wait
|
|
298
|
+
// for.
|
|
299
|
+
toast.componentRef.changeDetectorRef.detectChanges();
|
|
300
|
+
const destroy = () => {
|
|
301
|
+
toast.componentRef.destroy();
|
|
302
|
+
if (this.active.length === 0 && this.container?.isConnected) {
|
|
303
|
+
this.container.remove();
|
|
304
|
+
this.container = null;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
// The host is the toast element itself, and the leave is an opacity transition on it.
|
|
308
|
+
const element = toast.componentRef.location.nativeElement;
|
|
309
|
+
// Wait for the transition, but never depend on it: an element that is display:none, in a
|
|
310
|
+
// background tab, or under a zero-duration transition fires no `transitionend` at all, and a
|
|
311
|
+
// toast that is never destroyed leaks a view. The timeout matches the 300ms in the stylesheet.
|
|
312
|
+
let done = false;
|
|
313
|
+
const finish = () => {
|
|
314
|
+
if (done)
|
|
315
|
+
return;
|
|
316
|
+
done = true;
|
|
317
|
+
element.removeEventListener('transitionend', finish);
|
|
318
|
+
destroy();
|
|
319
|
+
};
|
|
320
|
+
element.addEventListener('transitionend', finish);
|
|
321
|
+
setTimeout(finish, 350);
|
|
322
|
+
}
|
|
323
|
+
ensureContainer(positionClass) {
|
|
324
|
+
if (this.container?.isConnected) {
|
|
325
|
+
this.container.className = `nxe-toast-container ${positionClass}`;
|
|
326
|
+
return this.container;
|
|
327
|
+
}
|
|
328
|
+
const container = this.document.createElement('div');
|
|
329
|
+
container.className = `nxe-toast-container ${positionClass}`;
|
|
330
|
+
// Deliberately no `aria-live` here: each toast carries its own role, and a live region around
|
|
331
|
+
// them would make every toast announce twice.
|
|
332
|
+
this.document.body.appendChild(container);
|
|
333
|
+
this.container = container;
|
|
334
|
+
return container;
|
|
335
|
+
}
|
|
336
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
337
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeToastService, providedIn: 'root' });
|
|
338
|
+
}
|
|
339
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeToastService, decorators: [{
|
|
340
|
+
type: Injectable,
|
|
341
|
+
args: [{ providedIn: 'root' }]
|
|
342
|
+
}] });
|
|
343
|
+
|
|
344
|
+
// @nexterajs/ui/notify — public API.
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Generated bundle index. Do not edit.
|
|
348
|
+
*/
|
|
349
|
+
|
|
350
|
+
export { NXE_TOAST_CONFIG, NXE_TOAST_DEFAULTS, NxeToastComponent, NxeToastService, provideNxeToast };
|
|
351
|
+
//# sourceMappingURL=nexterajs-ui-notify.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nexterajs-ui-notify.mjs","sources":["../../../projects/nextera-ui/notify/src/lib/notify/nxe-toast.config.ts","../../../projects/nextera-ui/notify/src/lib/notify/nxe-toast.component.ts","../../../projects/nextera-ui/notify/src/lib/notify/nxe-toast.service.ts","../../../projects/nextera-ui/notify/src/public-api.ts","../../../projects/nextera-ui/notify/src/nexterajs-ui-notify.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\r\n\r\nexport type NxeToastType = 'success' | 'error' | 'warning' | 'info';\r\n\r\nexport type NxeToastPosition =\r\n | 'toast-top-center'\r\n | 'toast-bottom-center'\r\n | 'toast-top-right'\r\n | 'toast-bottom-right'\r\n | 'toast-top-left'\r\n | 'toast-bottom-left'\r\n | 'toast-top-full-width'\r\n | 'toast-bottom-full-width';\r\n\r\n/** Per-call options. Anything omitted falls back to the global {@link NxeToastConfig}. */\r\nexport interface NxeToastOverride {\r\n /**\r\n * Render the message as HTML rather than text.\r\n *\r\n * The message is still sanitised. Callers pass server-composed strings here — a list of\r\n * validation failures joined with `<br>` — so the markup is not author-controlled and must not\r\n * be trusted, but stripping it entirely would collapse those lists onto one line.\r\n */\r\n enableHtml?: boolean;\r\n /** Dismiss when the body is clicked. The close button works regardless. */\r\n tapToDismiss?: boolean;\r\n /** Milliseconds before auto-dismiss. `0` keeps it up until dismissed. */\r\n timeOut?: number;\r\n closeButton?: boolean;\r\n progressBar?: boolean;\r\n /** Extra classes on the toast element. */\r\n toastClass?: string;\r\n titleClass?: string;\r\n messageClass?: string;\r\n}\r\n\r\nexport interface NxeToastConfig extends NxeToastOverride {\r\n /** Which corner or edge the stack is anchored to. */\r\n positionClass?: NxeToastPosition;\r\n /**\r\n * Put the newest toast at the top of the stack.\r\n *\r\n * Defaults to true, matching the library this replaces. A naive appending list reverses it —\r\n * visibly, and without any test failing.\r\n */\r\n newestOnTop?: boolean;\r\n /** Drop a toast whose type, title and message match one already showing. */\r\n preventDuplicates?: boolean;\r\n /** Cap the stack; 0 means no cap. Older toasts are dismissed first. */\r\n maxOpened?: number;\r\n}\r\n\r\nexport const NXE_TOAST_DEFAULTS: Required<\r\n Pick<\r\n NxeToastConfig,\r\n | 'positionClass'\r\n | 'timeOut'\r\n | 'closeButton'\r\n | 'progressBar'\r\n | 'tapToDismiss'\r\n | 'enableHtml'\r\n | 'newestOnTop'\r\n | 'preventDuplicates'\r\n | 'maxOpened'\r\n >\r\n> = {\r\n positionClass: 'toast-top-right',\r\n timeOut: 5000,\r\n closeButton: false,\r\n progressBar: false,\r\n tapToDismiss: true,\r\n enableHtml: false,\r\n newestOnTop: true,\r\n preventDuplicates: false,\r\n maxOpened: 0,\r\n};\r\n\r\nexport const NXE_TOAST_CONFIG = new InjectionToken<NxeToastConfig>('NXE_TOAST_CONFIG', {\r\n providedIn: 'root',\r\n factory: () => ({}),\r\n});\r\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n inject,\r\n input,\r\n output,\r\n SecurityContext,\r\n signal,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { NxeToastType } from './nxe-toast.config';\r\n\r\n/**\r\n * One toast. Rendered by {@link NxeToastService} into its container; not used directly.\r\n *\r\n * **The element order is load-bearing.** The close button comes first and is floated right, which\r\n * is how the library this replaces positioned it — move it after the title and it drops below the\r\n * text instead of sitting in the corner. Likewise the host carries the classes rather than an\r\n * inner wrapper, because the shipped stylesheet targets `.nxe-toast-container .nxe-toast`\r\n * directly and an extra element between them changes what `width` and `margin` apply to.\r\n *\r\n * Styles are global (`@nexterajs/ui/styles/_toast.scss`), not encapsulated, so an application's\r\n * existing overrides keep winning on specificity exactly as they did before.\r\n *\r\n * Accessibility, which the original had none of:\r\n * - `error` and `warning` announce as `alert` (assertive — the user's own action caused it).\r\n * `success` and `info` announce as `status` (polite).\r\n * - Focus is never taken. A validation toast typically appears mid-keystroke, and moving focus\r\n * would pull the caret out of the field being corrected.\r\n * - The close button is a real `<button>` with a label, reachable by keyboard.\r\n */\r\n@Component({\r\n selector: 'nxe-toast',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: {\r\n '[class]': 'hostClass()',\r\n '[attr.role]': 'assertive() ? \"alert\" : \"status\"',\r\n '[attr.aria-live]': 'assertive() ? \"assertive\" : \"polite\"',\r\n 'aria-atomic': 'true',\r\n '(click)': 'onBodyClick()',\r\n },\r\n template: `\r\n @if (closeButton()) {\r\n <button type=\"button\" class=\"nxe-toast-close\" aria-label=\"Close notification\" (click)=\"onCloseClick($event)\">\r\n ×\r\n </button>\r\n }\r\n\r\n @if (title()) {\r\n <div [class]=\"'nxe-toast-title ' + titleClass()\" [attr.aria-label]=\"title()\">{{ title() }}</div>\r\n }\r\n\r\n @if (message()) {\r\n @if (enableHtml()) {\r\n <div [class]=\"'nxe-toast-message ' + messageClass()\" [innerHTML]=\"safeMessage()\"></div>\r\n } @else {\r\n <div [class]=\"'nxe-toast-message ' + messageClass()\">{{ message() }}</div>\r\n }\r\n }\r\n\r\n @if (progressBar()) {\r\n <div><div class=\"nxe-toast-progress\" [style.width.%]=\"progressWidth()\"></div></div>\r\n }\r\n `,\r\n})\r\nexport class NxeToastComponent {\r\n private readonly sanitizer = inject(DomSanitizer);\r\n\r\n readonly type = input.required<NxeToastType>();\r\n readonly title = input<string | null>(null);\r\n readonly message = input<string | null>(null);\r\n readonly enableHtml = input(false);\r\n readonly tapToDismiss = input(true);\r\n readonly closeButton = input(false);\r\n readonly progressBar = input(false);\r\n readonly toastClass = input('');\r\n readonly titleClass = input('');\r\n readonly messageClass = input('');\r\n\r\n readonly dismissed = output<void>();\r\n\r\n /** Pre-enter state: the service clears it a frame later so the opacity transition runs. */\r\n readonly entering = signal(true);\r\n /** Set while the leave transition plays; the service removes the view once it finishes. */\r\n readonly dismissing = signal(false);\r\n readonly progressWidth = signal(100);\r\n\r\n protected readonly assertive = computed(\r\n () => this.type() === 'error' || this.type() === 'warning',\r\n );\r\n\r\n protected readonly hostClass = computed(() =>\r\n [\r\n 'nxe-toast',\r\n `nxe-toast-${this.type()}`,\r\n this.toastClass(),\r\n this.entering() ? 'nxe-toast-entering' : '',\r\n this.dismissing() ? 'nxe-toast-leaving' : '',\r\n ]\r\n .filter(Boolean)\r\n .join(' '),\r\n );\r\n\r\n /**\r\n * Sanitised HTML for the message.\r\n *\r\n * Callers pass server-composed strings here — validation failures joined with `<br>` — so this\r\n * is data, not markup the application authored. `sanitize` drops anything executable and keeps\r\n * the line breaks the lists depend on.\r\n */\r\n protected readonly safeMessage = computed<SafeHtml>(\r\n () => this.sanitizer.sanitize(SecurityContext.HTML, this.message() ?? '') ?? '',\r\n );\r\n\r\n protected onBodyClick(): void {\r\n if (this.tapToDismiss()) this.dismissed.emit();\r\n }\r\n\r\n protected onCloseClick(event: Event): void {\r\n // Without this the host click handler also fires, dismissing twice.\r\n event.stopPropagation();\r\n this.dismissed.emit();\r\n }\r\n}\r\n","import { DOCUMENT } from '@angular/common';\r\nimport {\r\n ApplicationRef,\r\n ComponentRef,\r\n createComponent,\r\n EnvironmentInjector,\r\n EnvironmentProviders,\r\n inject,\r\n Injectable,\r\n makeEnvironmentProviders,\r\n} from '@angular/core';\r\nimport { NxeToastComponent } from './nxe-toast.component';\r\nimport {\r\n NXE_TOAST_CONFIG,\r\n NXE_TOAST_DEFAULTS,\r\n NxeToastConfig,\r\n NxeToastOverride,\r\n NxeToastType,\r\n} from './nxe-toast.config';\r\n\r\n/** Handle on a shown toast. `dismiss()` is safe to call more than once. */\r\nexport interface NxeToastRef {\r\n dismiss(): void;\r\n readonly type: NxeToastType;\r\n readonly title: string | null;\r\n readonly message: string | null;\r\n}\r\n\r\ninterface ActiveToast extends NxeToastRef {\r\n componentRef: ComponentRef<NxeToastComponent>;\r\n timer: ReturnType<typeof setTimeout> | null;\r\n key: string;\r\n}\r\n\r\n/**\r\n * Configure the toast stack once, at bootstrap.\r\n *\r\n * ```ts\r\n * providers: [provideNxeToast({ positionClass: 'toast-top-center' })]\r\n * ```\r\n */\r\nexport function provideNxeToast(config: NxeToastConfig = {}): EnvironmentProviders {\r\n return makeEnvironmentProviders([{ provide: NXE_TOAST_CONFIG, useValue: config }]);\r\n}\r\n\r\n/**\r\n * Shows toasts.\r\n *\r\n * **The container is deliberately not a CDK overlay.** A CDK overlay pane lands in the same\r\n * `.cdk-overlay-container` as the application's dialogs, which makes paint order creation order —\r\n * so a toast raised right after a dialog closes can end up behind the next dialog that opens.\r\n * Callers do exactly that. A dedicated container with its own stacking context keeps toasts above\r\n * dialogs regardless of the order things were created in.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class NxeToastService {\r\n private readonly appRef = inject(ApplicationRef);\r\n private readonly injector = inject(EnvironmentInjector);\r\n private readonly document = inject(DOCUMENT);\r\n private readonly userConfig = inject(NXE_TOAST_CONFIG);\r\n\r\n private readonly config: Required<\r\n Pick<\r\n NxeToastConfig,\r\n | 'positionClass'\r\n | 'timeOut'\r\n | 'closeButton'\r\n | 'progressBar'\r\n | 'tapToDismiss'\r\n | 'enableHtml'\r\n | 'newestOnTop'\r\n | 'preventDuplicates'\r\n | 'maxOpened'\r\n >\r\n > &\r\n NxeToastConfig = { ...NXE_TOAST_DEFAULTS, ...this.userConfig };\r\n\r\n private container: HTMLElement | null = null;\r\n private readonly active: ActiveToast[] = [];\r\n\r\n success(message?: string | null, title?: string | null, override?: NxeToastOverride): NxeToastRef {\r\n return this.show('success', message, title, override);\r\n }\r\n\r\n error(message?: string | null, title?: string | null, override?: NxeToastOverride): NxeToastRef {\r\n return this.show('error', message, title, override);\r\n }\r\n\r\n warning(message?: string | null, title?: string | null, override?: NxeToastOverride): NxeToastRef {\r\n return this.show('warning', message, title, override);\r\n }\r\n\r\n info(message?: string | null, title?: string | null, override?: NxeToastOverride): NxeToastRef {\r\n return this.show('info', message, title, override);\r\n }\r\n\r\n /** Dismiss everything currently showing. */\r\n clear(): void {\r\n for (const toast of [...this.active]) this.remove(toast);\r\n }\r\n\r\n show(\r\n type: NxeToastType,\r\n message?: string | null,\r\n title?: string | null,\r\n override?: NxeToastOverride,\r\n ): NxeToastRef {\r\n const options = { ...this.config, ...(override ?? {}) };\r\n const key = `${type}|${title ?? ''}|${message ?? ''}`;\r\n\r\n if (options.preventDuplicates) {\r\n const existing = this.active.find((toast) => toast.key === key);\r\n if (existing) return existing;\r\n }\r\n\r\n // Trim the oldest first: the newest toast is the one the user's last action produced.\r\n if (options.maxOpened > 0) {\r\n while (this.active.length >= options.maxOpened) this.remove(this.active[0]);\r\n }\r\n\r\n const componentRef = createComponent(NxeToastComponent, {\r\n environmentInjector: this.injector,\r\n });\r\n\r\n componentRef.setInput('type', type);\r\n componentRef.setInput('title', title ?? null);\r\n componentRef.setInput('message', message ?? null);\r\n componentRef.setInput('enableHtml', options.enableHtml);\r\n componentRef.setInput('tapToDismiss', options.tapToDismiss);\r\n componentRef.setInput('closeButton', options.closeButton);\r\n componentRef.setInput('progressBar', options.progressBar);\r\n componentRef.setInput('toastClass', options.toastClass ?? '');\r\n componentRef.setInput('titleClass', options.titleClass ?? '');\r\n componentRef.setInput('messageClass', options.messageClass ?? '');\r\n\r\n const toast: ActiveToast = {\r\n componentRef,\r\n timer: null,\r\n key,\r\n type,\r\n title: title ?? null,\r\n message: message ?? null,\r\n dismiss: () => this.remove(toast),\r\n };\r\n\r\n componentRef.instance.dismissed.subscribe(() => this.remove(toast));\r\n\r\n const host = this.ensureContainer(options.positionClass);\r\n // `newestOnTop` is prepend-vs-append, and getting it backwards is invisible to every test\r\n // while being immediately obvious to anyone who has used the application.\r\n if (options.newestOnTop) host.insertBefore(componentRef.location.nativeElement, host.firstChild);\r\n else host.appendChild(componentRef.location.nativeElement);\r\n\r\n this.appRef.attachView(componentRef.hostView);\r\n // This view is created imperatively rather than by a template, so nothing else will render it\r\n // before the next scheduled tick. Render it now: a toast that appears a frame late is a\r\n // flicker, and callers legitimately read the DOM straight after raising one.\r\n componentRef.changeDetectorRef.detectChanges();\r\n this.active.push(toast);\r\n\r\n // The toast mounts at opacity 0 and transitions in. Clearing the flag in a later frame is what\r\n // gives the browser a starting value to animate from — set it in the same frame and there is\r\n // no transition at all, just an instant appearance.\r\n requestAnimationFrame(() => {\r\n if (!componentRef.hostView.destroyed) {\r\n componentRef.instance.entering.set(false);\r\n componentRef.changeDetectorRef.detectChanges();\r\n }\r\n });\r\n\r\n if (options.timeOut > 0) {\r\n toast.timer = setTimeout(() => this.remove(toast), options.timeOut);\r\n if (options.progressBar) this.runProgress(toast, options.timeOut);\r\n }\r\n\r\n return toast;\r\n }\r\n\r\n /** Shrink the progress bar across the toast's lifetime, the way the original drove it. */\r\n private runProgress(toast: ActiveToast, timeOut: number): void {\r\n const started = performance.now();\r\n const tick = (now: number): void => {\r\n if (toast.componentRef.hostView.destroyed) return;\r\n const remaining = Math.max(0, 100 - ((now - started) / timeOut) * 100);\r\n toast.componentRef.instance.progressWidth.set(remaining);\r\n toast.componentRef.changeDetectorRef.detectChanges();\r\n if (remaining > 0) requestAnimationFrame(tick);\r\n };\r\n requestAnimationFrame(tick);\r\n }\r\n\r\n private remove(toast: ActiveToast): void {\r\n const index = this.active.indexOf(toast);\r\n if (index === -1) return;\r\n\r\n this.active.splice(index, 1);\r\n if (toast.timer !== null) clearTimeout(toast.timer);\r\n\r\n toast.componentRef.instance.dismissing.set(true);\r\n // Flush the leave class now rather than on the next tick, so the animation starts in the same\r\n // frame as the dismissal and the `animationend` below is guaranteed to have something to wait\r\n // for.\r\n toast.componentRef.changeDetectorRef.detectChanges();\r\n\r\n const destroy = (): void => {\r\n toast.componentRef.destroy();\r\n if (this.active.length === 0 && this.container?.isConnected) {\r\n this.container.remove();\r\n this.container = null;\r\n }\r\n };\r\n\r\n // The host is the toast element itself, and the leave is an opacity transition on it.\r\n const element = toast.componentRef.location.nativeElement as HTMLElement;\r\n\r\n // Wait for the transition, but never depend on it: an element that is display:none, in a\r\n // background tab, or under a zero-duration transition fires no `transitionend` at all, and a\r\n // toast that is never destroyed leaks a view. The timeout matches the 300ms in the stylesheet.\r\n let done = false;\r\n const finish = (): void => {\r\n if (done) return;\r\n done = true;\r\n element.removeEventListener('transitionend', finish);\r\n destroy();\r\n };\r\n element.addEventListener('transitionend', finish);\r\n setTimeout(finish, 350);\r\n }\r\n\r\n private ensureContainer(positionClass: string): HTMLElement {\r\n if (this.container?.isConnected) {\r\n this.container.className = `nxe-toast-container ${positionClass}`;\r\n return this.container;\r\n }\r\n\r\n const container = this.document.createElement('div');\r\n container.className = `nxe-toast-container ${positionClass}`;\r\n // Deliberately no `aria-live` here: each toast carries its own role, and a live region around\r\n // them would make every toast announce twice.\r\n this.document.body.appendChild(container);\r\n this.container = container;\r\n return container;\r\n }\r\n}\r\n","// @nexterajs/ui/notify — public API.\r\nexport * from './lib/notify/nxe-toast.config';\r\nexport * from './lib/notify/nxe-toast.component';\r\nexport * from './lib/notify/nxe-toast.service';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|