@photonix/basic 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6768,7 +6768,7 @@ var Switch = React233.forwardRef(
6768
6768
  Switch.displayName = "Switch";
6769
6769
 
6770
6770
  // ../ultimate/src/components/text-field/TextField.tsx
6771
- var React239 = __toESM(require("react"));
6771
+ var React240 = __toESM(require("react"));
6772
6772
  var import_react_dom2 = require("react-dom");
6773
6773
 
6774
6774
  // ../ultimate/src/components/divider/Divider.tsx
@@ -6809,11 +6809,613 @@ var Divider = React234.forwardRef(
6809
6809
  );
6810
6810
  Divider.displayName = "Divider";
6811
6811
 
6812
+ // ../ultimate/src/hooks/useNumberSuggestions.ts
6813
+ var React235 = __toESM(require("react"));
6814
+
6815
+ // ../ultimate/src/utils/format.utils.ts
6816
+ var formatCurrency = (value, separator = ",", allowDecimal = true, maxDecimals = 2) => {
6817
+ if (!value) return "";
6818
+ const decimalChar = separator === "," ? "." : ",";
6819
+ let cleaned = value.replace(new RegExp(`[^\\d\\${decimalChar}-]`, "g"), "");
6820
+ const isNegative = cleaned.startsWith("-");
6821
+ cleaned = cleaned.replace(/-/g, "");
6822
+ const parts = cleaned.split(decimalChar);
6823
+ let integerPart = parts[0] ?? "";
6824
+ const decimalPart = parts[1];
6825
+ integerPart = integerPart.replace(/^0+(?=\d)/, "");
6826
+ if (integerPart === "") integerPart = "0";
6827
+ integerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
6828
+ let result = integerPart;
6829
+ if (allowDecimal && decimalPart !== void 0) {
6830
+ result += decimalChar + decimalPart.slice(0, maxDecimals);
6831
+ }
6832
+ if (isNegative && result !== "0") {
6833
+ result = "-" + result;
6834
+ }
6835
+ return result;
6836
+ };
6837
+ var VN_CALLING_CODE = "84";
6838
+ var E164_MAX_DIGITS = 15;
6839
+ var chunkDigits = (digits, sizes) => {
6840
+ const groups = [];
6841
+ let index = 0;
6842
+ for (const size of sizes) {
6843
+ if (index >= digits.length) break;
6844
+ groups.push(digits.slice(index, index + size));
6845
+ index += size;
6846
+ }
6847
+ return groups;
6848
+ };
6849
+ var genericPhoneGroups = (length) => {
6850
+ if (length <= 5) return [length];
6851
+ const threes = Math.floor(length / 3);
6852
+ switch (length % 3) {
6853
+ case 0:
6854
+ return Array(threes).fill(3);
6855
+ case 1:
6856
+ return [...Array(threes - 1).fill(3), 4];
6857
+ default:
6858
+ return [...Array(threes - 2).fill(3), 4, 4];
6859
+ }
6860
+ };
6861
+ var groupNationalNumber = (nsn, callingCode) => {
6862
+ if (callingCode === VN_CALLING_CODE) {
6863
+ if (/^[35789]/.test(nsn)) return chunkDigits(nsn.slice(0, 9), [3, 3, 3]);
6864
+ if (/^2[48]/.test(nsn)) return chunkDigits(nsn.slice(0, 10), [2, 4, 4]);
6865
+ if (nsn.startsWith("2")) return chunkDigits(nsn.slice(0, 10), [3, 3, 4]);
6866
+ }
6867
+ const capped = nsn.slice(0, E164_MAX_DIGITS - callingCode.length);
6868
+ return chunkDigits(capped, genericPhoneGroups(capped.length));
6869
+ };
6870
+ var parsePhone = (value, options = {}) => {
6871
+ const trimmed = value.trim();
6872
+ let digits = trimmed.replace(/\D/g, "");
6873
+ let hasPlus = trimmed.startsWith("+");
6874
+ if (!hasPlus && digits.startsWith("00")) {
6875
+ hasPlus = true;
6876
+ digits = digits.slice(2);
6877
+ }
6878
+ const fixedCode = options.countryCode?.replace(/\D/g, "");
6879
+ if (fixedCode) {
6880
+ if (hasPlus && digits.startsWith(fixedCode)) digits = digits.slice(fixedCode.length);
6881
+ return { kind: "national", callingCode: fixedCode, nsn: digits.replace(/^0/, "") };
6882
+ }
6883
+ if (hasPlus) {
6884
+ if (!digits.startsWith(VN_CALLING_CODE)) return { kind: "unknown", digits: digits.slice(0, E164_MAX_DIGITS) };
6885
+ return { kind: "international", callingCode: VN_CALLING_CODE, nsn: digits.slice(2).replace(/^0/, "") };
6886
+ }
6887
+ if (/^1[89]00/.test(digits)) return { kind: "service", digits: digits.slice(0, 10) };
6888
+ if (digits.startsWith("0")) return { kind: "trunk", callingCode: VN_CALLING_CODE, nsn: digits.slice(1) };
6889
+ return { kind: "national", callingCode: VN_CALLING_CODE, nsn: digits };
6890
+ };
6891
+ var formatPhone = (value, options) => {
6892
+ if (!value) return "";
6893
+ const parsed = parsePhone(value, options);
6894
+ switch (parsed.kind) {
6895
+ case "unknown":
6896
+ return `+${parsed.digits}`;
6897
+ case "service":
6898
+ return chunkDigits(parsed.digits, parsed.digits.length <= 8 ? [4, 4] : [4, 3, 3]).join(" ");
6899
+ case "international":
6900
+ return [`+${parsed.callingCode}`, ...groupNationalNumber(parsed.nsn, parsed.callingCode)].join(" ");
6901
+ case "trunk": {
6902
+ const groups = groupNationalNumber(parsed.nsn, parsed.callingCode);
6903
+ return groups.length ? `0${groups.join(" ")}` : "0";
6904
+ }
6905
+ case "national":
6906
+ return groupNationalNumber(parsed.nsn, parsed.callingCode).join(" ");
6907
+ }
6908
+ };
6909
+ var normalizePhone = (value, options) => {
6910
+ if (!value) return "";
6911
+ const parsed = parsePhone(value, options);
6912
+ switch (parsed.kind) {
6913
+ case "unknown":
6914
+ return parsed.digits ? `+${parsed.digits}` : "";
6915
+ case "service":
6916
+ return parsed.digits;
6917
+ default: {
6918
+ const nsn = groupNationalNumber(parsed.nsn, parsed.callingCode).join("");
6919
+ return nsn ? `+${parsed.callingCode}${nsn}` : "";
6920
+ }
6921
+ }
6922
+ };
6923
+ var BANK_ACCOUNT_MAX_DIGITS = 19;
6924
+ var formatBankAccount = (value) => {
6925
+ if (!value) return "";
6926
+ const digits = value.replace(/\D/g, "").slice(0, BANK_ACCOUNT_MAX_DIGITS);
6927
+ const groups = digits.match(/\d{1,4}/g) ?? [];
6928
+ if (groups.length > 1 && groups[groups.length - 1].length === 1) {
6929
+ const last = groups.pop();
6930
+ groups[groups.length - 1] += last;
6931
+ }
6932
+ return groups.join(" ");
6933
+ };
6934
+ var formatCardNumber = (value) => {
6935
+ if (!value) return "";
6936
+ const digits = value.replace(/[^0-9]/g, "").slice(0, 16);
6937
+ return digits.replace(/(\d{4})(?=\d)/g, "$1 ").trim();
6938
+ };
6939
+ var calculateCursorPosition = (prevValue, nextValue, prevCursorPos) => {
6940
+ if (prevCursorPos <= 0) return 0;
6941
+ const rawBefore = prevValue.slice(0, prevCursorPos).replace(/[^0-9]/g, "");
6942
+ const rawDigitCount = rawBefore.length;
6943
+ let digitsSeen = 0;
6944
+ for (let i = 0; i < nextValue.length; i++) {
6945
+ if (/\d/.test(nextValue[i])) {
6946
+ digitsSeen++;
6947
+ }
6948
+ if (digitsSeen === rawDigitCount) {
6949
+ return i + 1;
6950
+ }
6951
+ }
6952
+ return nextValue.length;
6953
+ };
6954
+
6955
+ // ../ultimate/src/utils/number-detect.utils.ts
6956
+ var NUMBER_SUGGESTION_THRESHOLD = 0.4;
6957
+ var CONFIDENT_SCORE = 0.7;
6958
+ var FALLBACK_CUTOFF = 0.5;
6959
+ var ACCOUNT_LABELS = [
6960
+ "stk",
6961
+ "so tk",
6962
+ "so tai khoan",
6963
+ "tai khoan",
6964
+ "tk",
6965
+ "tknh",
6966
+ "account",
6967
+ "acct",
6968
+ "acc",
6969
+ "a/c",
6970
+ "ck",
6971
+ "chuyen khoan",
6972
+ "chuyen tien",
6973
+ "chuyen vao",
6974
+ "nhan tien",
6975
+ "thu huong"
6976
+ ];
6977
+ var BANK_NAMES = [
6978
+ "vietcombank",
6979
+ "vcb",
6980
+ "techcombank",
6981
+ "tcb",
6982
+ "mbbank",
6983
+ "mb bank",
6984
+ "mb",
6985
+ "bidv",
6986
+ "vietinbank",
6987
+ "vietin",
6988
+ "ctg",
6989
+ "agribank",
6990
+ "acb",
6991
+ "vpbank",
6992
+ "vpb",
6993
+ "tpbank",
6994
+ "tpb",
6995
+ "sacombank",
6996
+ "stb",
6997
+ "hdbank",
6998
+ "hdb",
6999
+ "vib",
7000
+ "shb",
7001
+ "msb",
7002
+ "ocb",
7003
+ "seabank",
7004
+ "eximbank",
7005
+ "eib",
7006
+ "lpbank",
7007
+ "lienvietpostbank",
7008
+ "namabank",
7009
+ "nam a bank",
7010
+ "abbank",
7011
+ "pvcombank",
7012
+ "baovietbank",
7013
+ "bac a bank",
7014
+ "bacabank",
7015
+ "kienlongbank",
7016
+ "vietbank",
7017
+ "vietabank",
7018
+ "saigonbank",
7019
+ "scb",
7020
+ "pgbank",
7021
+ "ncb",
7022
+ "gpbank",
7023
+ "oceanbank",
7024
+ "cbbank",
7025
+ "uob",
7026
+ "hsbc",
7027
+ "shinhan",
7028
+ "woori",
7029
+ "citibank",
7030
+ "cake",
7031
+ "timo",
7032
+ "ubank",
7033
+ "ngan hang",
7034
+ "bank"
7035
+ ];
7036
+ var PHONE_LABELS = [
7037
+ "sdt",
7038
+ "so dt",
7039
+ "so dien thoai",
7040
+ "dien thoai",
7041
+ "dt",
7042
+ "dd",
7043
+ "di dong",
7044
+ "phone",
7045
+ "tel",
7046
+ "mobile",
7047
+ "hotline",
7048
+ "zalo",
7049
+ "viber",
7050
+ "whatsapp",
7051
+ "telegram",
7052
+ "sms",
7053
+ "lien he",
7054
+ "lh",
7055
+ "call",
7056
+ "nhan tin"
7057
+ ];
7058
+ var MONEY_MARKERS = [
7059
+ "d",
7060
+ "vnd",
7061
+ "dong",
7062
+ "k",
7063
+ "tr",
7064
+ "trieu",
7065
+ "ty",
7066
+ "nghin",
7067
+ "ngan",
7068
+ "usd",
7069
+ "dola",
7070
+ "xu",
7071
+ "%"
7072
+ ];
7073
+ var isDigit = (c) => c !== void 0 && c >= "0" && c <= "9";
7074
+ var isLetter = (c) => c !== void 0 && /\p{L}/u.test(c);
7075
+ var isAlnum = (c) => isDigit(c) || isLetter(c);
7076
+ var isGapChar = (c) => c === " " || c === "." || c === "-" || c === "\xA0" || c === "(" || c === ")";
7077
+ var foldChar = (c) => {
7078
+ if (c === "\u0111" || c === "\u0110" || c === "\u20AB") return "d";
7079
+ const base = c.normalize("NFD")[0] ?? c;
7080
+ return base.toLowerCase();
7081
+ };
7082
+ var fold = (text) => {
7083
+ let out = "";
7084
+ for (let i = 0; i < text.length; i++) out += foldChar(text[i]);
7085
+ return out;
7086
+ };
7087
+ var readRuns = (text) => {
7088
+ const runs = [];
7089
+ let i = 0;
7090
+ while (i < text.length) {
7091
+ let j = i;
7092
+ if (text[j] === "(") j++;
7093
+ const plus = text[j] === "+";
7094
+ if (plus) j++;
7095
+ if (!isDigit(text[j]) || j === i && !isDigit(text[i])) {
7096
+ i++;
7097
+ continue;
7098
+ }
7099
+ const start = i;
7100
+ const glued = isAlnum(text[start - 1]) || text[start - 1] === "+";
7101
+ const groups = [];
7102
+ let gap = "";
7103
+ let k = j;
7104
+ for (; ; ) {
7105
+ const groupStart = k;
7106
+ while (isDigit(text[k])) k++;
7107
+ groups.push({ digits: text.slice(groupStart, k), start: groupStart, end: k, gap });
7108
+ let g = k;
7109
+ while (g < text.length && g - k < 2 && isGapChar(text[g])) g++;
7110
+ if (g > k && isDigit(text[g])) {
7111
+ gap = text.slice(k, g);
7112
+ k = g;
7113
+ continue;
7114
+ }
7115
+ break;
7116
+ }
7117
+ i = k;
7118
+ if (glued || isLetter(text[k])) continue;
7119
+ runs.push({ plus, start, groups });
7120
+ }
7121
+ return runs;
7122
+ };
7123
+ var phoneShape = (digits, plus) => {
7124
+ let international = plus;
7125
+ let d = digits;
7126
+ if (!plus && d.startsWith("0084")) {
7127
+ international = true;
7128
+ d = d.slice(2);
7129
+ }
7130
+ if (international) {
7131
+ if (d.startsWith("84")) {
7132
+ const nsn = d.slice(2).replace(/^0/, "");
7133
+ return /^[35789]\d{8}$/.test(nsn) || /^2\d{9}$/.test(nsn) ? "strong" : null;
7134
+ }
7135
+ return d.length >= 8 && d.length <= 15 ? "weak" : null;
7136
+ }
7137
+ if (/^0[35789]\d{8}$/.test(d) || /^02\d{9}$/.test(d) || /^1[89]00(\d{4}|\d{6})$/.test(d)) return "strong";
7138
+ if (/^84[35789]\d{8}$/.test(d)) return "weak";
7139
+ return null;
7140
+ };
7141
+ var isCompletePhone = (digits, plus) => plus ? /^84[35789]\d{8}$/.test(digits) : /^0[35789]\d{8}$/.test(digits) || /^02\d{9}$/.test(digits);
7142
+ var ACCOUNT_MAX_DIGITS = 19;
7143
+ var E164_MAX_DIGITS2 = 15;
7144
+ var splitRun = (run) => {
7145
+ const segments = [];
7146
+ let current = [run.groups[0]];
7147
+ let plus = run.plus;
7148
+ let start = run.start;
7149
+ const close = () => {
7150
+ segments.push({
7151
+ plus,
7152
+ start,
7153
+ end: current[current.length - 1].end,
7154
+ groups: current,
7155
+ digits: current.map((g) => g.digits).join("")
7156
+ });
7157
+ };
7158
+ for (const group of run.groups.slice(1)) {
7159
+ const digits = current.map((g) => g.digits).join("");
7160
+ const max = plus ? E164_MAX_DIGITS2 : ACCOUNT_MAX_DIGITS;
7161
+ const breaks = digits.length >= 8 && group.digits.length >= 6 || isCompletePhone(digits, plus) && group.digits.startsWith("0") || digits.length + group.digits.length > max;
7162
+ if (breaks) {
7163
+ close();
7164
+ current = [group];
7165
+ plus = false;
7166
+ start = group.gap.endsWith("(") ? group.start - 1 : group.start;
7167
+ } else {
7168
+ current.push(group);
7169
+ }
7170
+ }
7171
+ close();
7172
+ return segments;
7173
+ };
7174
+ var looksLikeAmount = (segment) => {
7175
+ const [first, ...rest] = segment.groups;
7176
+ if (!first || rest.length === 0) return false;
7177
+ if (first.digits.length > 3 || first.digits.startsWith("0")) return false;
7178
+ return rest.every((g) => g.digits.length === 3 && g.gap === ".");
7179
+ };
7180
+ var startsWithWord = (folded, at, words) => words.some((w) => folded.startsWith(w, at) && !isAlnum(folded[at + w.length]));
7181
+ var isMoney = (text, folded, segment) => {
7182
+ if (looksLikeAmount(segment)) return true;
7183
+ if (segment.plus || segment.digits.startsWith("0")) return false;
7184
+ let before = segment.start - 1;
7185
+ while (before >= 0 && folded[before] === " ") before--;
7186
+ if (folded[before] === "$") return true;
7187
+ let after = segment.end;
7188
+ if ((folded[after] === "," || folded[after] === ".") && isDigit(folded[after + 1])) {
7189
+ after++;
7190
+ while (isDigit(folded[after])) after++;
7191
+ }
7192
+ while (after < folded.length && folded[after] === " ") after++;
7193
+ if (text[after] === "$") return true;
7194
+ return startsWithWord(folded, after, MONEY_MARKERS);
7195
+ };
7196
+ var BEFORE_WINDOW = 40;
7197
+ var AFTER_WINDOW = 28;
7198
+ var lastWordEnd = (folded, from, to, words) => {
7199
+ let best = -1;
7200
+ for (const w of words) {
7201
+ let at = folded.indexOf(w, from);
7202
+ while (at !== -1 && at + w.length <= to) {
7203
+ const end = at + w.length;
7204
+ if (!isAlnum(folded[at - 1]) && !isAlnum(folded[end]) && end > best) best = end;
7205
+ at = folded.indexOf(w, at + 1);
7206
+ }
7207
+ }
7208
+ return best;
7209
+ };
7210
+ var firstWordStart = (folded, from, to, words) => {
7211
+ let best = -1;
7212
+ for (const w of words) {
7213
+ const at = folded.indexOf(w, from);
7214
+ if (at === -1 || at + w.length > to) continue;
7215
+ if (!isAlnum(folded[at - 1]) && !isAlnum(folded[at + w.length]) && (best === -1 || at < best)) best = at;
7216
+ }
7217
+ return best;
7218
+ };
7219
+ var readContext = (folded, segment, prevEnd, nextStart) => {
7220
+ let lineStart = folded.lastIndexOf("\n", segment.start - 1) + 1;
7221
+ if (folded.slice(lineStart, segment.start).trim() === "" && lineStart >= 2) {
7222
+ lineStart = folded.lastIndexOf("\n", lineStart - 2) + 1;
7223
+ }
7224
+ const from = Math.max(lineStart, prevEnd, segment.start - BEFORE_WINDOW);
7225
+ const accountEnd = Math.max(
7226
+ lastWordEnd(folded, from, segment.start, ACCOUNT_LABELS),
7227
+ lastWordEnd(folded, from, segment.start, BANK_NAMES)
7228
+ );
7229
+ const phoneEnd = lastWordEnd(folded, from, segment.start, PHONE_LABELS);
7230
+ if (accountEnd !== -1 || phoneEnd !== -1) return accountEnd > phoneEnd ? "bankAccount" : "phone";
7231
+ let lineEnd = folded.indexOf("\n", segment.end);
7232
+ if (lineEnd === -1) lineEnd = folded.length;
7233
+ const to = Math.min(lineEnd, nextStart, segment.end + AFTER_WINDOW);
7234
+ return firstWordStart(folded, segment.end, to, BANK_NAMES) !== -1 ? "bankAccount" : null;
7235
+ };
7236
+ var scoreSegment = (segment, context) => {
7237
+ const { digits, plus } = segment;
7238
+ const shape = phoneShape(digits, plus);
7239
+ const scores = {};
7240
+ if (shape) {
7241
+ if (context === "phone") scores.phone = 1;
7242
+ else if (context === null) scores.phone = shape === "strong" ? 0.8 : 0.5;
7243
+ } else if (context === "phone" && digits.length >= 8 && digits.length <= E164_MAX_DIGITS2) {
7244
+ scores.phone = 0.6;
7245
+ }
7246
+ const international = plus || shape !== null && digits.startsWith("0084");
7247
+ const minAccount = context === "bankAccount" ? 6 : 8;
7248
+ if (!international && digits.length >= minAccount && digits.length <= ACCOUNT_MAX_DIGITS) {
7249
+ if (context === "bankAccount") scores.bankAccount = 1;
7250
+ else if (context === null) scores.bankAccount = shape ? 0.4 : 0.7;
7251
+ }
7252
+ return scores;
7253
+ };
7254
+ var detectNumbers = (input) => {
7255
+ if (!input) return [];
7256
+ const text = input.normalize("NFC");
7257
+ const folded = fold(text);
7258
+ const segments = readRuns(text).flatMap(splitRun);
7259
+ const result = [];
7260
+ segments.forEach((segment, index) => {
7261
+ if (isMoney(text, folded, segment)) return;
7262
+ const prevEnd = index > 0 ? segments[index - 1].end : 0;
7263
+ const nextStart = index < segments.length - 1 ? segments[index + 1].start : text.length;
7264
+ const scores = scoreSegment(segment, readContext(folded, segment, prevEnd, nextStart));
7265
+ if (scores.phone === void 0 && scores.bankAccount === void 0) return;
7266
+ const value = (segment.plus ? "+" : "") + segment.digits;
7267
+ const best = (scores.phone ?? 0) >= (scores.bankAccount ?? 0) ? "phone" : "bankAccount";
7268
+ result.push({
7269
+ value,
7270
+ formatted: best === "phone" ? formatPhone(value) : formatBankAccount(value),
7271
+ text: text.slice(segment.start, segment.end),
7272
+ start: segment.start,
7273
+ end: segment.end,
7274
+ scores
7275
+ });
7276
+ });
7277
+ return result;
7278
+ };
7279
+ var numberKey = (value, kind) => kind === "phone" ? normalizePhone(value) || value.replace(/\D/g, "") : value.replace(/\D/g, "");
7280
+ var suggestNumbers = (text, kind, options = {}) => {
7281
+ const seen = /* @__PURE__ */ new Map();
7282
+ for (const detected of detectNumbers(text)) {
7283
+ const found = { ...detected, formatted: kind === "phone" ? formatPhone(detected.value) : formatBankAccount(detected.value) };
7284
+ const score = found.scores[kind];
7285
+ if (score === void 0 || score < NUMBER_SUGGESTION_THRESHOLD) continue;
7286
+ const key = numberKey(found.value, kind);
7287
+ const known = seen.get(key);
7288
+ if (!known) seen.set(key, found);
7289
+ else if ((known.scores[kind] ?? 0) < score) seen.set(key, { ...found, start: known.start, end: known.end });
7290
+ }
7291
+ let ranked = [...seen.values()].sort(
7292
+ (a, b) => (b.scores[kind] ?? 0) - (a.scores[kind] ?? 0) || a.start - b.start
7293
+ );
7294
+ if ((ranked[0]?.scores[kind] ?? 0) >= CONFIDENT_SCORE) {
7295
+ ranked = ranked.filter((found) => (found.scores[kind] ?? 0) >= FALLBACK_CUTOFF);
7296
+ }
7297
+ return options.limit === void 0 ? ranked : ranked.slice(0, options.limit);
7298
+ };
7299
+ var isTextWithNumbers = (text) => {
7300
+ if (!text || !/\d/.test(text)) return false;
7301
+ if (/[\p{L}\n]/u.test(text)) return true;
7302
+ return detectNumbers(text).length > 1;
7303
+ };
7304
+
7305
+ // ../ultimate/src/hooks/useNumberSuggestions.ts
7306
+ var source = "";
7307
+ var listeners = /* @__PURE__ */ new Set();
7308
+ var emit = () => listeners.forEach((listener) => listener());
7309
+ var setNumberSuggestionSource = (text) => {
7310
+ if (text === source) return;
7311
+ source = text;
7312
+ emit();
7313
+ };
7314
+ var copyListenerCount = 0;
7315
+ var handleCopy = (event) => {
7316
+ const target = event.target;
7317
+ let text = "";
7318
+ if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {
7319
+ text = target.value.slice(target.selectionStart ?? 0, target.selectionEnd ?? 0);
7320
+ } else {
7321
+ text = window.getSelection()?.toString() ?? "";
7322
+ }
7323
+ if (/\d/.test(text) && suggestNumbersAny(text)) setNumberSuggestionSource(text);
7324
+ };
7325
+ var suggestNumbersAny = (text) => suggestNumbers(text, "phone", { limit: 1 }).length > 0 || suggestNumbers(text, "bankAccount", { limit: 1 }).length > 0;
7326
+ var subscribe = (listener) => {
7327
+ listeners.add(listener);
7328
+ if (typeof document !== "undefined" && copyListenerCount++ === 0) {
7329
+ document.addEventListener("copy", handleCopy, true);
7330
+ }
7331
+ return () => {
7332
+ listeners.delete(listener);
7333
+ if (typeof document !== "undefined" && --copyListenerCount === 0) {
7334
+ document.removeEventListener("copy", handleCopy, true);
7335
+ }
7336
+ };
7337
+ };
7338
+ var getSnapshot = () => source;
7339
+ var getServerSnapshot = () => "";
7340
+ var clipboardAccess = async () => {
7341
+ if (typeof navigator === "undefined" || !navigator.clipboard?.readText) return "none";
7342
+ try {
7343
+ const status = await navigator.permissions?.query({ name: "clipboard-read" });
7344
+ if (status?.state === "granted") return "granted";
7345
+ if (status?.state === "denied") return "none";
7346
+ } catch {
7347
+ }
7348
+ return "ask";
7349
+ };
7350
+ var readClipboard = async () => {
7351
+ try {
7352
+ return await navigator.clipboard.readText() ?? "";
7353
+ } catch {
7354
+ return "";
7355
+ }
7356
+ };
7357
+ var useNumberSuggestions = ({
7358
+ kind,
7359
+ value,
7360
+ onSelect,
7361
+ enabled = true,
7362
+ clipboard = true,
7363
+ limit = 5
7364
+ }) => {
7365
+ const text = React235.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
7366
+ const [canPasteFromClipboard, setCanPasteFromClipboard] = React235.useState(false);
7367
+ const onSelectRef = React235.useRef(onSelect);
7368
+ onSelectRef.current = onSelect;
7369
+ const suggestions = React235.useMemo(() => {
7370
+ if (!enabled || !text || limit <= 0) return [];
7371
+ const current = value ? numberKey(value, kind) : "";
7372
+ return suggestNumbers(text, kind).filter((found) => numberKey(found.value, kind) !== current).slice(0, limit);
7373
+ }, [enabled, text, value, kind, limit]);
7374
+ const take = React235.useCallback(
7375
+ (incoming, fill) => {
7376
+ setNumberSuggestionSource(incoming);
7377
+ const [best] = suggestNumbers(incoming, kind, { limit: 1 });
7378
+ if (fill && best) onSelectRef.current(best.value);
7379
+ },
7380
+ [kind]
7381
+ );
7382
+ const onPaste = React235.useCallback(
7383
+ (event) => {
7384
+ if (!enabled) return;
7385
+ const pasted = event.clipboardData.getData("text");
7386
+ if (!isTextWithNumbers(pasted)) return;
7387
+ event.preventDefault();
7388
+ take(pasted, true);
7389
+ },
7390
+ [enabled, take]
7391
+ );
7392
+ const onFocus = React235.useCallback(() => {
7393
+ if (!enabled || !clipboard) return;
7394
+ void clipboardAccess().then(async (access) => {
7395
+ if (access === "granted") {
7396
+ const read = await readClipboard();
7397
+ if (read && read !== source && isTextWithNumbers(read)) take(read, false);
7398
+ setCanPasteFromClipboard(false);
7399
+ } else {
7400
+ setCanPasteFromClipboard(access === "ask");
7401
+ }
7402
+ });
7403
+ }, [enabled, clipboard, take]);
7404
+ const pasteFromClipboard = React235.useCallback(async () => {
7405
+ const read = await readClipboard();
7406
+ setCanPasteFromClipboard(false);
7407
+ if (!read) return;
7408
+ if (isTextWithNumbers(read)) take(read, !value);
7409
+ else if (/\d/.test(read)) onSelectRef.current(read.trim());
7410
+ }, [take, value]);
7411
+ return { suggestions, onPaste, onFocus, canPasteFromClipboard, pasteFromClipboard };
7412
+ };
7413
+
6812
7414
  // ../ultimate/src/components/dropdown-list/DropdownList.tsx
6813
- var React237 = __toESM(require("react"));
7415
+ var React238 = __toESM(require("react"));
6814
7416
 
6815
7417
  // ../ultimate/src/components/search-field/SearchField.tsx
6816
- var React236 = __toESM(require("react"));
7418
+ var React237 = __toESM(require("react"));
6817
7419
 
6818
7420
  // ../ultimate/src/hooks/useComposedRefs.ts
6819
7421
  var import_react3 = require("react");
@@ -6865,9 +7467,9 @@ function useClickOutside(ref, handler, enabled = true, ignoredRefs = []) {
6865
7467
  var import_react6 = require("react");
6866
7468
 
6867
7469
  // ../ultimate/src/internal/overlay/OverlayScope.tsx
6868
- var React235 = __toESM(require("react"));
7470
+ var React236 = __toESM(require("react"));
6869
7471
  var import_jsx_runtime232 = require("react/jsx-runtime");
6870
- var OverlayDepthContext = React235.createContext(0);
7472
+ var OverlayDepthContext = React236.createContext(0);
6871
7473
 
6872
7474
  // ../ultimate/src/hooks/useEscapeKeydown.ts
6873
7475
  var layersByDocument = /* @__PURE__ */ new WeakMap();
@@ -7120,7 +7722,7 @@ var SearchField_default = {};
7120
7722
 
7121
7723
  // ../ultimate/src/components/search-field/SearchField.tsx
7122
7724
  var import_jsx_runtime233 = require("react/jsx-runtime");
7123
- var SearchField = React236.forwardRef(
7725
+ var SearchField = React237.forwardRef(
7124
7726
  ({
7125
7727
  placeholder = "Search",
7126
7728
  variant = "default",
@@ -7140,16 +7742,19 @@ var SearchField = React236.forwardRef(
7140
7742
  defaultValue,
7141
7743
  fullWidth = true,
7142
7744
  width,
7745
+ bare = false,
7746
+ loading = false,
7747
+ onKeyDown,
7143
7748
  ...props
7144
7749
  }, ref) => {
7145
- const [focused, setFocused] = React236.useState(false);
7750
+ const [focused, setFocused] = React237.useState(false);
7146
7751
  const [valueState, setValueState] = useControllableState({
7147
7752
  value,
7148
7753
  defaultValue: defaultValue || "",
7149
7754
  onChange
7150
7755
  });
7151
- const inputRef = React236.useRef(null);
7152
- React236.useEffect(() => {
7756
+ const inputRef = React237.useRef(null);
7757
+ React237.useEffect(() => {
7153
7758
  if (!shortcutHint || disabled) return;
7154
7759
  const handleGlobalKeyDown = (e) => {
7155
7760
  if (!e.isComposing && !e.defaultPrevented && (e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
@@ -7160,7 +7765,7 @@ var SearchField = React236.forwardRef(
7160
7765
  window.addEventListener("keydown", handleGlobalKeyDown);
7161
7766
  return () => window.removeEventListener("keydown", handleGlobalKeyDown);
7162
7767
  }, [shortcutHint, disabled]);
7163
- React236.useImperativeHandle(ref, () => inputRef.current);
7768
+ React237.useImperativeHandle(ref, () => inputRef.current);
7164
7769
  const currentValue = valueState;
7165
7770
  const handleFocus = (e) => {
7166
7771
  setFocused(true);
@@ -7189,7 +7794,8 @@ var SearchField = React236.forwardRef(
7189
7794
  inputRef.current?.focus();
7190
7795
  };
7191
7796
  const handleKeyDown = (e) => {
7192
- if (disabled || e.nativeEvent.isComposing) return;
7797
+ onKeyDown?.(e);
7798
+ if (e.defaultPrevented || disabled || e.nativeEvent.isComposing) return;
7193
7799
  if (e.key === "Enter") {
7194
7800
  e.preventDefault();
7195
7801
  focusNextElement(e.currentTarget);
@@ -7204,6 +7810,7 @@ var SearchField = React236.forwardRef(
7204
7810
  SearchField_default[size],
7205
7811
  !fullWidth && !width && SearchField_default.widthAuto,
7206
7812
  variant === "rounded" && SearchField_default.searchFieldRounded,
7813
+ bare && SearchField_default.bare,
7207
7814
  focused && SearchField_default.searchFieldFocused,
7208
7815
  disabled && SearchField_default.searchFieldDisabled,
7209
7816
  className
@@ -7211,7 +7818,7 @@ var SearchField = React236.forwardRef(
7211
7818
  style: { ...style, width },
7212
7819
  onClick: () => !disabled && inputRef.current?.focus(),
7213
7820
  children: [
7214
- /* @__PURE__ */ (0, import_jsx_runtime233.jsx)("span", { className: SearchField_default.searchFieldIcon, children: /* @__PURE__ */ (0, import_jsx_runtime233.jsx)(SearchOutline, { size: size === "large" ? 24 : 20 }) }),
7821
+ /* @__PURE__ */ (0, import_jsx_runtime233.jsx)("span", { className: SearchField_default.searchFieldIcon, children: loading ? /* @__PURE__ */ (0, import_jsx_runtime233.jsx)(Spinner2, { size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime233.jsx)(SearchOutline, { size: size === "large" ? 24 : 20 }) }),
7215
7822
  /* @__PURE__ */ (0, import_jsx_runtime233.jsx)(
7216
7823
  "input",
7217
7824
  {
@@ -7227,6 +7834,7 @@ var SearchField = React236.forwardRef(
7227
7834
  value: currentValue,
7228
7835
  placeholder,
7229
7836
  type: "search",
7837
+ "aria-busy": loading || void 0,
7230
7838
  ...props
7231
7839
  }
7232
7840
  ),
@@ -7272,7 +7880,7 @@ function getItemId(collectionId, part, key) {
7272
7880
 
7273
7881
  // ../ultimate/src/components/dropdown-list/DropdownList.tsx
7274
7882
  var import_jsx_runtime234 = require("react/jsx-runtime");
7275
- var DropdownList = React237.forwardRef(
7883
+ var DropdownList = React238.forwardRef(
7276
7884
  ({
7277
7885
  items,
7278
7886
  role = "listbox",
@@ -7290,32 +7898,32 @@ var DropdownList = React237.forwardRef(
7290
7898
  className,
7291
7899
  style
7292
7900
  }, ref) => {
7293
- const typeaheadRef = React237.useRef({ text: "", time: 0 });
7294
- const [searchQuery, setSearchQuery] = React237.useState("");
7901
+ const typeaheadRef = React238.useRef({ text: "", time: 0 });
7902
+ const [searchQuery, setSearchQuery] = React238.useState("");
7295
7903
  const showSearch = role === "listbox" && items.length >= 10;
7296
- const instanceId = React237.useId();
7904
+ const instanceId = React238.useId();
7297
7905
  const itemId = (value2) => value2 === void 0 ? void 0 : getItemId(instanceId, "item", value2);
7298
- const selectedValues = React237.useMemo(() => {
7906
+ const selectedValues = React238.useMemo(() => {
7299
7907
  if (Array.isArray(value)) return value;
7300
7908
  return value ? [value] : [];
7301
7909
  }, [value]);
7302
- const filteredItems = React237.useMemo(() => {
7910
+ const filteredItems = React238.useMemo(() => {
7303
7911
  if (!searchQuery.trim()) return items;
7304
7912
  const query = searchQuery.toLowerCase();
7305
7913
  return items.filter(
7306
7914
  (item) => item.label.toLowerCase().includes(query)
7307
7915
  );
7308
7916
  }, [items, searchQuery]);
7309
- const selectableFilteredValues = React237.useMemo(() => filteredItems.filter((item) => !item.disabled).map((item) => item.value), [filteredItems]);
7917
+ const selectableFilteredValues = React238.useMemo(() => filteredItems.filter((item) => !item.disabled).map((item) => item.value), [filteredItems]);
7310
7918
  const showSelectAll = mode === "multi" && Boolean(selectAllLabel) && selectableFilteredValues.length > 0;
7311
7919
  const allFilteredSelected = showSelectAll && selectableFilteredValues.every((itemValue) => selectedValues.includes(itemValue));
7312
7920
  const someFilteredSelected = showSelectAll && !allFilteredSelected && selectableFilteredValues.some((itemValue) => selectedValues.includes(itemValue));
7313
- const handleSelectAllClick = React237.useCallback(() => {
7921
+ const handleSelectAllClick = React238.useCallback(() => {
7314
7922
  if (!showSelectAll) return;
7315
7923
  const nextValues = allFilteredSelected ? selectedValues.filter((itemValue) => !selectableFilteredValues.includes(itemValue)) : Array.from(/* @__PURE__ */ new Set([...selectedValues, ...selectableFilteredValues]));
7316
7924
  onChange?.(nextValues);
7317
7925
  }, [allFilteredSelected, onChange, selectableFilteredValues, selectedValues, showSelectAll]);
7318
- const displayItems = React237.useMemo(() => {
7926
+ const displayItems = React238.useMemo(() => {
7319
7927
  if (!showSelectAll || !selectAllLabel) return filteredItems;
7320
7928
  return [
7321
7929
  { value: "__phx_select_all__", label: selectAllLabel, __selectAll: true },
@@ -7338,10 +7946,10 @@ var DropdownList = React237.forwardRef(
7338
7946
  const isSelected = (itemValue) => {
7339
7947
  return selectedValues.includes(itemValue);
7340
7948
  };
7341
- const [showTopGradient, setShowTopGradient] = React237.useState(false);
7342
- const [showBottomGradient, setShowBottomGradient] = React237.useState(false);
7343
- const listRef = React237.useRef(null);
7344
- const handleScroll = React237.useCallback(() => {
7949
+ const [showTopGradient, setShowTopGradient] = React238.useState(false);
7950
+ const [showBottomGradient, setShowBottomGradient] = React238.useState(false);
7951
+ const listRef = React238.useRef(null);
7952
+ const handleScroll = React238.useCallback(() => {
7345
7953
  const el = listRef.current;
7346
7954
  if (!el) return;
7347
7955
  const scrollTop = el.scrollTop;
@@ -7350,7 +7958,7 @@ var DropdownList = React237.forwardRef(
7350
7958
  setShowTopGradient(scrollTop > 10);
7351
7959
  setShowBottomGradient(scrollTop + clientHeight < scrollHeight - 10);
7352
7960
  }, []);
7353
- React237.useEffect(() => {
7961
+ React238.useEffect(() => {
7354
7962
  handleScroll();
7355
7963
  }, [displayItems, handleScroll]);
7356
7964
  const ITEM_HEIGHT = size === "sm" ? 32 : 48;
@@ -7360,9 +7968,9 @@ var DropdownList = React237.forwardRef(
7360
7968
  itemHeight: ITEM_HEIGHT,
7361
7969
  containerHeight: parsedMaxHeight
7362
7970
  });
7363
- const [focusedIndex, setFocusedIndex] = React237.useState(-1);
7364
- const [isKeyboardMode, setIsKeyboardMode] = React237.useState(false);
7365
- React237.useEffect(() => {
7971
+ const [focusedIndex, setFocusedIndex] = React238.useState(-1);
7972
+ const [isKeyboardMode, setIsKeyboardMode] = React238.useState(false);
7973
+ React238.useEffect(() => {
7366
7974
  if (displayItems.length === 0) {
7367
7975
  setFocusedIndex(-1);
7368
7976
  return;
@@ -7379,7 +7987,7 @@ var DropdownList = React237.forwardRef(
7379
7987
  return prev;
7380
7988
  });
7381
7989
  }, [displayItems, value, mode]);
7382
- React237.useEffect(() => {
7990
+ React238.useEffect(() => {
7383
7991
  if (focusedIndex !== -1 && listRef.current) {
7384
7992
  const itemTop = focusedIndex * ITEM_HEIGHT;
7385
7993
  const itemBottom = itemTop + ITEM_HEIGHT;
@@ -7393,7 +8001,7 @@ var DropdownList = React237.forwardRef(
7393
8001
  }
7394
8002
  }
7395
8003
  }, [focusedIndex, ITEM_HEIGHT]);
7396
- const handleListScroll = React237.useCallback((e) => {
8004
+ const handleListScroll = React238.useCallback((e) => {
7397
8005
  handleScroll();
7398
8006
  if (isVirtual) onVirtualScroll(e);
7399
8007
  }, [handleScroll, isVirtual, onVirtualScroll]);
@@ -7549,7 +8157,7 @@ var DropdownList = React237.forwardRef(
7549
8157
  tabIndex: -1
7550
8158
  }
7551
8159
  ),
7552
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime234.jsx)("span", { className: DropdownList_default.dropdownListItemIcon, children: React237.isValidElement(item.icon) ? React237.cloneElement(item.icon, { size: size === "sm" ? 20 : 24 }) : item.icon }),
8160
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime234.jsx)("span", { className: DropdownList_default.dropdownListItemIcon, children: React238.isValidElement(item.icon) ? React238.cloneElement(item.icon, { size: size === "sm" ? 20 : 24 }) : item.icon }),
7553
8161
  /* @__PURE__ */ (0, import_jsx_runtime234.jsx)("span", { className: DropdownList_default.dropdownListItemLabel, children: item.label }),
7554
8162
  mode === "single" && isSelected(item.value) && /* @__PURE__ */ (0, import_jsx_runtime234.jsx)("span", { className: DropdownList_default.dropdownListCheckmark, children: /* @__PURE__ */ (0, import_jsx_runtime234.jsx)(CheckSmallOutline, { size: size === "sm" ? 20 : 24 }) })
7555
8163
  ]
@@ -7596,7 +8204,7 @@ var DropdownList = React237.forwardRef(
7596
8204
  tabIndex: -1
7597
8205
  }
7598
8206
  ),
7599
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime234.jsx)("span", { className: DropdownList_default.dropdownListItemIcon, children: React237.isValidElement(item.icon) ? React237.cloneElement(item.icon, { size: size === "sm" ? 20 : 24 }) : item.icon }),
8207
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime234.jsx)("span", { className: DropdownList_default.dropdownListItemIcon, children: React238.isValidElement(item.icon) ? React238.cloneElement(item.icon, { size: size === "sm" ? 20 : 24 }) : item.icon }),
7600
8208
  /* @__PURE__ */ (0, import_jsx_runtime234.jsx)("span", { className: DropdownList_default.dropdownListItemLabel, children: item.label }),
7601
8209
  mode === "single" && isSelected(item.value) && /* @__PURE__ */ (0, import_jsx_runtime234.jsx)("span", { className: DropdownList_default.dropdownListCheckmark, children: /* @__PURE__ */ (0, import_jsx_runtime234.jsx)(CheckSmallOutline, { size: size === "sm" ? 20 : 24 }) })
7602
8210
  ]
@@ -7621,137 +8229,11 @@ var DropdownList = React237.forwardRef(
7621
8229
  );
7622
8230
  DropdownList.displayName = "DropdownList";
7623
8231
 
7624
- // ../ultimate/src/utils/format.utils.ts
7625
- var formatCurrency = (value, separator = ",", allowDecimal = true, maxDecimals = 2) => {
7626
- if (!value) return "";
7627
- const decimalChar = separator === "," ? "." : ",";
7628
- let cleaned = value.replace(new RegExp(`[^\\d\\${decimalChar}-]`, "g"), "");
7629
- const isNegative = cleaned.startsWith("-");
7630
- cleaned = cleaned.replace(/-/g, "");
7631
- const parts = cleaned.split(decimalChar);
7632
- let integerPart = parts[0] ?? "";
7633
- const decimalPart = parts[1];
7634
- integerPart = integerPart.replace(/^0+(?=\d)/, "");
7635
- if (integerPart === "") integerPart = "0";
7636
- integerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
7637
- let result = integerPart;
7638
- if (allowDecimal && decimalPart !== void 0) {
7639
- result += decimalChar + decimalPart.slice(0, maxDecimals);
7640
- }
7641
- if (isNegative && result !== "0") {
7642
- result = "-" + result;
7643
- }
7644
- return result;
7645
- };
7646
- var VN_CALLING_CODE = "84";
7647
- var E164_MAX_DIGITS = 15;
7648
- var chunkDigits = (digits, sizes) => {
7649
- const groups = [];
7650
- let index = 0;
7651
- for (const size of sizes) {
7652
- if (index >= digits.length) break;
7653
- groups.push(digits.slice(index, index + size));
7654
- index += size;
7655
- }
7656
- return groups;
7657
- };
7658
- var genericPhoneGroups = (length) => {
7659
- if (length <= 5) return [length];
7660
- const threes = Math.floor(length / 3);
7661
- switch (length % 3) {
7662
- case 0:
7663
- return Array(threes).fill(3);
7664
- case 1:
7665
- return [...Array(threes - 1).fill(3), 4];
7666
- default:
7667
- return [...Array(threes - 2).fill(3), 4, 4];
7668
- }
7669
- };
7670
- var groupNationalNumber = (nsn, callingCode) => {
7671
- if (callingCode === VN_CALLING_CODE) {
7672
- if (/^[35789]/.test(nsn)) return chunkDigits(nsn.slice(0, 9), [3, 3, 3]);
7673
- if (/^2[48]/.test(nsn)) return chunkDigits(nsn.slice(0, 10), [2, 4, 4]);
7674
- if (nsn.startsWith("2")) return chunkDigits(nsn.slice(0, 10), [3, 3, 4]);
7675
- }
7676
- const capped = nsn.slice(0, E164_MAX_DIGITS - callingCode.length);
7677
- return chunkDigits(capped, genericPhoneGroups(capped.length));
7678
- };
7679
- var parsePhone = (value, options = {}) => {
7680
- const trimmed = value.trim();
7681
- let digits = trimmed.replace(/\D/g, "");
7682
- let hasPlus = trimmed.startsWith("+");
7683
- if (!hasPlus && digits.startsWith("00")) {
7684
- hasPlus = true;
7685
- digits = digits.slice(2);
7686
- }
7687
- const fixedCode = options.countryCode?.replace(/\D/g, "");
7688
- if (fixedCode) {
7689
- if (hasPlus && digits.startsWith(fixedCode)) digits = digits.slice(fixedCode.length);
7690
- return { kind: "national", callingCode: fixedCode, nsn: digits.replace(/^0/, "") };
7691
- }
7692
- if (hasPlus) {
7693
- if (!digits.startsWith(VN_CALLING_CODE)) return { kind: "unknown", digits: digits.slice(0, E164_MAX_DIGITS) };
7694
- return { kind: "international", callingCode: VN_CALLING_CODE, nsn: digits.slice(2).replace(/^0/, "") };
7695
- }
7696
- if (/^1[89]00/.test(digits)) return { kind: "service", digits: digits.slice(0, 10) };
7697
- if (digits.startsWith("0")) return { kind: "trunk", callingCode: VN_CALLING_CODE, nsn: digits.slice(1) };
7698
- return { kind: "national", callingCode: VN_CALLING_CODE, nsn: digits };
7699
- };
7700
- var formatPhone = (value, options) => {
7701
- if (!value) return "";
7702
- const parsed = parsePhone(value, options);
7703
- switch (parsed.kind) {
7704
- case "unknown":
7705
- return `+${parsed.digits}`;
7706
- case "service":
7707
- return chunkDigits(parsed.digits, parsed.digits.length <= 8 ? [4, 4] : [4, 3, 3]).join(" ");
7708
- case "international":
7709
- return [`+${parsed.callingCode}`, ...groupNationalNumber(parsed.nsn, parsed.callingCode)].join(" ");
7710
- case "trunk": {
7711
- const groups = groupNationalNumber(parsed.nsn, parsed.callingCode);
7712
- return groups.length ? `0${groups.join(" ")}` : "0";
7713
- }
7714
- case "national":
7715
- return groupNationalNumber(parsed.nsn, parsed.callingCode).join(" ");
7716
- }
7717
- };
7718
- var BANK_ACCOUNT_MAX_DIGITS = 19;
7719
- var formatBankAccount = (value) => {
7720
- if (!value) return "";
7721
- const digits = value.replace(/\D/g, "").slice(0, BANK_ACCOUNT_MAX_DIGITS);
7722
- const groups = digits.match(/\d{1,4}/g) ?? [];
7723
- if (groups.length > 1 && groups[groups.length - 1].length === 1) {
7724
- const last = groups.pop();
7725
- groups[groups.length - 1] += last;
7726
- }
7727
- return groups.join(" ");
7728
- };
7729
- var formatCardNumber = (value) => {
7730
- if (!value) return "";
7731
- const digits = value.replace(/[^0-9]/g, "").slice(0, 16);
7732
- return digits.replace(/(\d{4})(?=\d)/g, "$1 ").trim();
7733
- };
7734
- var calculateCursorPosition = (prevValue, nextValue, prevCursorPos) => {
7735
- if (prevCursorPos <= 0) return 0;
7736
- const rawBefore = prevValue.slice(0, prevCursorPos).replace(/[^0-9]/g, "");
7737
- const rawDigitCount = rawBefore.length;
7738
- let digitsSeen = 0;
7739
- for (let i = 0; i < nextValue.length; i++) {
7740
- if (/\d/.test(nextValue[i])) {
7741
- digitsSeen++;
7742
- }
7743
- if (digitsSeen === rawDigitCount) {
7744
- return i + 1;
7745
- }
7746
- }
7747
- return nextValue.length;
7748
- };
7749
-
7750
8232
  // ../ultimate/src/components/text-field/TextField.module.css
7751
8233
  var TextField_default = {};
7752
8234
 
7753
8235
  // ../ultimate/src/internal/form/useNativeFieldReset.ts
7754
- var React238 = __toESM(require("react"));
8236
+ var React239 = __toESM(require("react"));
7755
8237
  function useNativeFieldReset(ref, resetState, resetValue, formId) {
7756
8238
  const reset = useCallbackRef((event) => {
7757
8239
  queueMicrotask(() => {
@@ -7760,7 +8242,7 @@ function useNativeFieldReset(ref, resetState, resetValue, formId) {
7760
8242
  ref.current.value = resetValue;
7761
8243
  });
7762
8244
  });
7763
- React238.useEffect(() => {
8245
+ React239.useEffect(() => {
7764
8246
  const form = ref.current?.form;
7765
8247
  form?.addEventListener("reset", reset);
7766
8248
  return () => form?.removeEventListener("reset", reset);
@@ -7769,7 +8251,7 @@ function useNativeFieldReset(ref, resetState, resetValue, formId) {
7769
8251
 
7770
8252
  // ../ultimate/src/components/text-field/TextField.tsx
7771
8253
  var import_jsx_runtime235 = require("react/jsx-runtime");
7772
- var TextField = React239.forwardRef(
8254
+ var TextField = React240.forwardRef(
7773
8255
  ({
7774
8256
  label,
7775
8257
  labelStyle = "outside",
@@ -7809,15 +8291,16 @@ var TextField = React239.forwardRef(
7809
8291
  maxLength,
7810
8292
  portal = true,
7811
8293
  selectOnFocus = false,
8294
+ smartPaste = true,
7812
8295
  ...props
7813
8296
  }, ref) => {
7814
- const generatedId = React239.useId();
8297
+ const generatedId = React240.useId();
7815
8298
  const inputId = props.id ?? generatedId;
7816
8299
  const helperId = `${inputId}-help`;
7817
- const [focused, setFocused] = React239.useState(false);
7818
- const [isDropdownOpen, setIsDropdownOpen] = React239.useState(false);
7819
- const [passwordVisible, setPasswordVisible] = React239.useState(false);
7820
- const inputRef = React239.useRef(null);
8300
+ const [focused, setFocused] = React240.useState(false);
8301
+ const [isDropdownOpen, setIsDropdownOpen] = React240.useState(false);
8302
+ const [passwordVisible, setPasswordVisible] = React240.useState(false);
8303
+ const inputRef = React240.useRef(null);
7821
8304
  const [valueState, setValueState, resetValueState] = useControllableState({
7822
8305
  value,
7823
8306
  defaultValue: defaultValue || "",
@@ -7835,8 +8318,8 @@ var TextField = React239.forwardRef(
7835
8318
  defaultValue: defaultTrailingDropdownValue || "",
7836
8319
  onChange: onTrailingDropdownChange
7837
8320
  });
7838
- const triggerWrapperRef = React239.useRef(null);
7839
- const popoverRef = React239.useRef(null);
8321
+ const triggerWrapperRef = React240.useRef(null);
8322
+ const popoverRef = React240.useRef(null);
7840
8323
  const { style: popoverStyle } = usePopoverPosition(triggerWrapperRef, popoverRef, isDropdownOpen, {
7841
8324
  margin: 8,
7842
8325
  strategy: portal ? "fixed" : "absolute",
@@ -7849,7 +8332,7 @@ var TextField = React239.forwardRef(
7849
8332
  [triggerWrapperRef]
7850
8333
  );
7851
8334
  useEscapeKeydown(() => setIsDropdownOpen(false), isDropdownOpen);
7852
- const keepSelectionOnMouseUpRef = React239.useRef(false);
8335
+ const keepSelectionOnMouseUpRef = React240.useRef(false);
7853
8336
  const handleFocus = (e) => {
7854
8337
  setFocused(true);
7855
8338
  if (selectOnFocus) e.currentTarget.select();
@@ -7866,14 +8349,14 @@ var TextField = React239.forwardRef(
7866
8349
  }
7867
8350
  props.onMouseUp?.(e);
7868
8351
  };
7869
- const isPhoneFormat = React239.useMemo(() => {
8352
+ const isPhoneFormat = React240.useMemo(() => {
7870
8353
  if (inputFormat) return false;
7871
8354
  if (!leadingText) return false;
7872
8355
  return (leadingText.startsWith("+") || /^\d+$/.test(leadingText.replace(/[\s()]/g, ""))) && !leadingText.includes("://");
7873
8356
  }, [leadingText, inputFormat]);
7874
8357
  const activeFormat = inputFormat || (isPhoneFormat ? "phone" : void 0);
7875
8358
  const isNumericFormat = activeFormat === "currency" || activeFormat === "phone" || activeFormat === "bankAccount" || activeFormat === "cardNumber";
7876
- const phoneCountryCode = React239.useMemo(() => {
8359
+ const phoneCountryCode = React240.useMemo(() => {
7877
8360
  if (activeFormat !== "phone") return void 0;
7878
8361
  if (formatOptions?.countryCode) return formatOptions.countryCode;
7879
8362
  const code = leadingText?.replace(/[\s()]/g, "");
@@ -7883,7 +8366,7 @@ var TextField = React239.forwardRef(
7883
8366
  const allowDecimal = formatOptions?.allowDecimal ?? true;
7884
8367
  const maxDigits = formatOptions?.maxDigits;
7885
8368
  const maxDecimals = formatOptions?.maxDecimals ?? 2;
7886
- const applyFormat = React239.useCallback((raw) => {
8369
+ const applyFormat = React240.useCallback((raw) => {
7887
8370
  switch (activeFormat) {
7888
8371
  case "currency": {
7889
8372
  let cleaned = raw;
@@ -7909,6 +8392,15 @@ var TextField = React239.forwardRef(
7909
8392
  return raw;
7910
8393
  }
7911
8394
  }, [activeFormat, separator, allowDecimal, maxDigits, maxDecimals, phoneCountryCode]);
8395
+ const suggestionKind = activeFormat === "phone" || activeFormat === "bankAccount" ? activeFormat : void 0;
8396
+ const numberSuggestions = useNumberSuggestions({
8397
+ kind: suggestionKind ?? "phone",
8398
+ value: String(currentValue ?? ""),
8399
+ enabled: Boolean(suggestionKind) && smartPaste && !disabled && !readOnly,
8400
+ clipboard: false,
8401
+ limit: 0,
8402
+ onSelect: (raw) => setValueState(applyFormat(raw))
8403
+ });
7912
8404
  const handleBlur = (e) => {
7913
8405
  setFocused(false);
7914
8406
  if (activeFormat && currentValue) {
@@ -7919,7 +8411,7 @@ var TextField = React239.forwardRef(
7919
8411
  }
7920
8412
  onBlur?.(e);
7921
8413
  };
7922
- React239.useEffect(() => {
8414
+ React240.useEffect(() => {
7923
8415
  if (activeFormat && currentValue) {
7924
8416
  const formatted = applyFormat(String(currentValue));
7925
8417
  if (currentValue !== formatted) {
@@ -8120,6 +8612,10 @@ var TextField = React239.forwardRef(
8120
8612
  onMouseDown: handleMouseDown,
8121
8613
  onMouseUp: handleMouseUp,
8122
8614
  onChange: handleChange,
8615
+ onPaste: (e) => {
8616
+ props.onPaste?.(e);
8617
+ if (!e.defaultPrevented) numberSuggestions.onPaste(e);
8618
+ },
8123
8619
  onKeyDown: handleKeyDown,
8124
8620
  value: currentValue,
8125
8621
  placeholder: shouldShowLabelInside ? "" : placeholder,
@@ -8248,14 +8744,14 @@ var TextField = React239.forwardRef(
8248
8744
  TextField.displayName = "TextField";
8249
8745
 
8250
8746
  // ../ultimate/src/components/text-area/TextArea.tsx
8251
- var React240 = __toESM(require("react"));
8747
+ var React241 = __toESM(require("react"));
8252
8748
 
8253
8749
  // ../ultimate/src/components/text-area/TextArea.module.css
8254
8750
  var TextArea_default = {};
8255
8751
 
8256
8752
  // ../ultimate/src/components/text-area/TextArea.tsx
8257
8753
  var import_jsx_runtime236 = require("react/jsx-runtime");
8258
- var TextArea = React240.forwardRef(
8754
+ var TextArea = React241.forwardRef(
8259
8755
  ({
8260
8756
  label,
8261
8757
  labelStyle = "outside",
@@ -8281,8 +8777,8 @@ var TextArea = React240.forwardRef(
8281
8777
  fullWidth = true,
8282
8778
  ...props
8283
8779
  }, ref) => {
8284
- const [focused, setFocused] = React240.useState(false);
8285
- const textareaRef = React240.useRef(null);
8780
+ const [focused, setFocused] = React241.useState(false);
8781
+ const textareaRef = React241.useRef(null);
8286
8782
  const [valueState, setValueState, resetValueState] = useControllableState({
8287
8783
  value,
8288
8784
  defaultValue: defaultValue || "",
@@ -8312,7 +8808,7 @@ var TextArea = React240.forwardRef(
8312
8808
  }
8313
8809
  setValueState(newValue);
8314
8810
  };
8315
- const generatedId = React240.useId();
8811
+ const generatedId = React241.useId();
8316
8812
  const shouldShowLabelOutside = label && labelStyle === "outside";
8317
8813
  const shouldShowLabelInside = label && labelStyle === "inside";
8318
8814
  const textareaId = id ?? generatedId;
@@ -8400,16 +8896,16 @@ var TextArea = React240.forwardRef(
8400
8896
  TextArea.displayName = "TextArea";
8401
8897
 
8402
8898
  // ../ultimate/src/components/avatar/Avatar.tsx
8403
- var React241 = __toESM(require("react"));
8899
+ var React242 = __toESM(require("react"));
8404
8900
 
8405
8901
  // ../ultimate/src/components/avatar/Avatar.module.css
8406
8902
  var Avatar_default = {};
8407
8903
 
8408
8904
  // ../ultimate/src/components/avatar/Avatar.tsx
8409
8905
  var import_jsx_runtime237 = require("react/jsx-runtime");
8410
- var Avatar = React241.forwardRef(
8906
+ var Avatar = React242.forwardRef(
8411
8907
  ({ src, alt, name, icon, size = "40", status, stroke, className, onClick, ...rest }, ref) => {
8412
- const [hasError, setHasError] = React241.useState(false);
8908
+ const [hasError, setHasError] = React242.useState(false);
8413
8909
  const isInteractive = !!onClick;
8414
8910
  const Component = isInteractive ? "button" : "div";
8415
8911
  const getInitials = (userName) => {
@@ -8467,16 +8963,16 @@ var Avatar = React241.forwardRef(
8467
8963
  Avatar.displayName = "Avatar";
8468
8964
 
8469
8965
  // ../ultimate/src/components/avatar/AvatarGroup.tsx
8470
- var React242 = __toESM(require("react"));
8966
+ var React243 = __toESM(require("react"));
8471
8967
 
8472
8968
  // ../ultimate/src/components/avatar/AvatarGroup.module.css
8473
8969
  var AvatarGroup_default = {};
8474
8970
 
8475
8971
  // ../ultimate/src/components/avatar/AvatarGroup.tsx
8476
8972
  var import_jsx_runtime238 = require("react/jsx-runtime");
8477
- var AvatarGroup = React242.forwardRef(
8973
+ var AvatarGroup = React243.forwardRef(
8478
8974
  ({ children, size = "40", max = 5, showStatus = false, className, ...rest }, ref) => {
8479
- const childrenArray = React242.Children.toArray(children);
8975
+ const childrenArray = React243.Children.toArray(children);
8480
8976
  const hasOverflow = childrenArray.length > max;
8481
8977
  const visibleCount = hasOverflow ? max - 1 : childrenArray.length;
8482
8978
  const visibleChildren = childrenArray.slice(0, visibleCount);
@@ -8489,8 +8985,8 @@ var AvatarGroup = React242.forwardRef(
8489
8985
  ].filter(Boolean).join(" ");
8490
8986
  return /* @__PURE__ */ (0, import_jsx_runtime238.jsxs)("div", { ref, className: groupClasses, ...rest, children: [
8491
8987
  visibleChildren.map((child, index) => {
8492
- if (React242.isValidElement(child)) {
8493
- return React242.cloneElement(child, {
8988
+ if (React243.isValidElement(child)) {
8989
+ return React243.cloneElement(child, {
8494
8990
  size,
8495
8991
  stroke: true,
8496
8992
  key: index,
@@ -8520,7 +9016,7 @@ var AvatarGroup = React242.forwardRef(
8520
9016
  AvatarGroup.displayName = "AvatarGroup";
8521
9017
 
8522
9018
  // ../ultimate/src/components/avatar/AvatarTemplate.tsx
8523
- var React243 = __toESM(require("react"));
9019
+ var React244 = __toESM(require("react"));
8524
9020
 
8525
9021
  // ../ultimate/src/img/Image=1.png
8526
9022
  var Image_1_default = "./Image=1-LTDZFXMH.png";
@@ -8552,7 +9048,7 @@ var toImageSrc = (image) => {
8552
9048
  return image.default;
8553
9049
  };
8554
9050
  var AVATAR_IMAGES = [Image_1_default, Image_2_default, Image_3_default, Image_4_default, Image_5_default, Image_6_default].map(toImageSrc);
8555
- var AvatarTemplate = React243.forwardRef(
9051
+ var AvatarTemplate = React244.forwardRef(
8556
9052
  ({ templateId = 1, ...rest }, ref) => {
8557
9053
  const src = AVATAR_IMAGES[templateId - 1];
8558
9054
  return /* @__PURE__ */ (0, import_jsx_runtime239.jsx)(Avatar, { ref, src, className: ["phx-avatar-template", rest.className].filter(Boolean).join(" "), ...rest });
@@ -8598,14 +9094,14 @@ var Image = ({
8598
9094
  };
8599
9095
 
8600
9096
  // ../ultimate/src/components/skeleton/Skeleton.tsx
8601
- var React244 = __toESM(require("react"));
9097
+ var React245 = __toESM(require("react"));
8602
9098
 
8603
9099
  // ../ultimate/src/components/skeleton/Skeleton.module.css
8604
9100
  var Skeleton_default = {};
8605
9101
 
8606
9102
  // ../ultimate/src/components/skeleton/Skeleton.tsx
8607
9103
  var import_jsx_runtime241 = require("react/jsx-runtime");
8608
- var Skeleton = React244.forwardRef(
9104
+ var Skeleton = React245.forwardRef(
8609
9105
  ({ variant = "text", width, height, rounded, className, style, ...rest }, ref) => {
8610
9106
  const skeletonClasses = [
8611
9107
  "phx-skeleton",
@@ -8635,17 +9131,18 @@ var Skeleton = React244.forwardRef(
8635
9131
  Skeleton.displayName = "Skeleton";
8636
9132
 
8637
9133
  // ../ultimate/src/components/chip/Chip.tsx
8638
- var React245 = __toESM(require("react"));
9134
+ var React246 = __toESM(require("react"));
8639
9135
 
8640
9136
  // ../ultimate/src/components/chip/Chip.module.css
8641
9137
  var Chip_default = {};
8642
9138
 
8643
9139
  // ../ultimate/src/components/chip/Chip.tsx
8644
9140
  var import_jsx_runtime242 = require("react/jsx-runtime");
8645
- var Chip = React245.forwardRef(
9141
+ var Chip = React246.forwardRef(
8646
9142
  ({
8647
9143
  label,
8648
9144
  icon,
9145
+ iconOnly,
8649
9146
  avatar,
8650
9147
  badge,
8651
9148
  variant = "outline",
@@ -8666,6 +9163,8 @@ var Chip = React245.forwardRef(
8666
9163
  const isClickable = !disabled;
8667
9164
  const hasBadgeValue = badge !== void 0 && String(badge).trim() !== "";
8668
9165
  const shouldShowBadge = !disabled && hasBadgeValue;
9166
+ const showsIconOnly = Boolean(iconOnly && icon && !avatar);
9167
+ const isIconSquare = showsIconOnly && !shouldShowBadge && !onRemove && !hasDropdown && !(actionIcon && onAction);
8669
9168
  const chipClasses = [
8670
9169
  "phx-chip",
8671
9170
  Chip_default.chipRoot,
@@ -8676,6 +9175,7 @@ var Chip = React245.forwardRef(
8676
9175
  isClickable && Chip_default.clickable,
8677
9176
  avatar && Chip_default.hasAvatar,
8678
9177
  shouldShowBadge && Chip_default.hasBadge,
9178
+ isIconSquare && Chip_default.iconSquare,
8679
9179
  className
8680
9180
  ].filter(Boolean).join(" ");
8681
9181
  const badgeColor = variant === "filled" ? "white" : "black";
@@ -8725,8 +9225,9 @@ var Chip = React245.forwardRef(
8725
9225
  ref,
8726
9226
  className: chipClasses,
8727
9227
  onClick: isClickable ? onClick : void 0,
8728
- role: isClickable ? "button" : void 0,
9228
+ role: isClickable ? "button" : showsIconOnly ? "img" : void 0,
8729
9229
  tabIndex: isClickable ? 0 : void 0,
9230
+ "aria-label": showsIconOnly ? label : void 0,
8730
9231
  ...rest,
8731
9232
  onKeyDown: (event) => {
8732
9233
  rest.onKeyDown?.(event);
@@ -8736,13 +9237,13 @@ var Chip = React245.forwardRef(
8736
9237
  }
8737
9238
  },
8738
9239
  children: /* @__PURE__ */ (0, import_jsx_runtime242.jsxs)("div", { className: Chip_default.chipContainer, children: [
8739
- avatar && /* @__PURE__ */ (0, import_jsx_runtime242.jsx)("div", { className: Chip_default.chipAvatar, children: React245.isValidElement(avatar) ? React245.cloneElement(avatar, {
9240
+ avatar && /* @__PURE__ */ (0, import_jsx_runtime242.jsx)("div", { className: Chip_default.chipAvatar, children: React246.isValidElement(avatar) ? React246.cloneElement(avatar, {
8740
9241
  size: size === "large" ? "32" : "24",
8741
9242
  stroke: variant === "filled" || avatar.props.stroke
8742
9243
  }) : avatar }),
8743
9244
  !avatar && icon && /* @__PURE__ */ (0, import_jsx_runtime242.jsx)("span", { className: Chip_default.chipIcon, children: icon }),
8744
- /* @__PURE__ */ (0, import_jsx_runtime242.jsxs)("span", { className: Chip_default.chipContent, children: [
8745
- /* @__PURE__ */ (0, import_jsx_runtime242.jsx)("span", { className: Chip_default.chipLabel, children: label }),
9245
+ (!showsIconOnly || shouldShowBadge) && /* @__PURE__ */ (0, import_jsx_runtime242.jsxs)("span", { className: Chip_default.chipContent, children: [
9246
+ !showsIconOnly && /* @__PURE__ */ (0, import_jsx_runtime242.jsx)("span", { className: Chip_default.chipLabel, children: label }),
8746
9247
  shouldShowBadge && /* @__PURE__ */ (0, import_jsx_runtime242.jsx)(
8747
9248
  Badge,
8748
9249
  {
@@ -8763,23 +9264,23 @@ var Chip = React245.forwardRef(
8763
9264
  Chip.displayName = "Chip";
8764
9265
 
8765
9266
  // ../ultimate/src/components/chip-bar/ChipBar.tsx
8766
- var React247 = __toESM(require("react"));
9267
+ var React248 = __toESM(require("react"));
8767
9268
 
8768
9269
  // ../ultimate/src/components/sticky-stack/StickyStack.tsx
8769
- var React246 = __toESM(require("react"));
9270
+ var React247 = __toESM(require("react"));
8770
9271
 
8771
9272
  // ../ultimate/src/components/sticky-stack/StickyStack.module.css
8772
9273
  var StickyStack_default = {};
8773
9274
 
8774
9275
  // ../ultimate/src/components/sticky-stack/StickyStack.tsx
8775
9276
  var import_jsx_runtime243 = require("react/jsx-runtime");
8776
- var BarSurfaceContext = React246.createContext(void 0);
8777
- var StickyStackItemContext = React246.createContext(false);
9277
+ var BarSurfaceContext = React247.createContext(void 0);
9278
+ var StickyStackItemContext = React247.createContext(false);
8778
9279
  function useInStickyStackItem() {
8779
- return React246.useContext(StickyStackItemContext);
9280
+ return React247.useContext(StickyStackItemContext);
8780
9281
  }
8781
9282
  function useBarSurface(surface) {
8782
- const inherited = React246.useContext(BarSurfaceContext);
9283
+ const inherited = React247.useContext(BarSurfaceContext);
8783
9284
  return surface ?? inherited;
8784
9285
  }
8785
9286
  function resolveStackSurfaces(entries) {
@@ -8790,26 +9291,26 @@ function resolveStackSurfaces(entries) {
8790
9291
  return covered ? "merged" : "elevated";
8791
9292
  });
8792
9293
  }
8793
- var StickyStackContext = React246.createContext(null);
9294
+ var StickyStackContext = React247.createContext(null);
8794
9295
  var findScrollParent = (element) => {
8795
9296
  for (let parent = element.parentElement; parent; parent = parent.parentElement) {
8796
9297
  if (/(auto|scroll|overlay)/.test(getComputedStyle(parent).overflowY)) return parent;
8797
9298
  }
8798
9299
  return null;
8799
9300
  };
8800
- var StickyStackItem = React246.forwardRef(
9301
+ var StickyStackItem = React247.forwardRef(
8801
9302
  ({ retracted = false, paint = true, onStuckChange, className, children, ...rest }, forwardedRef) => {
8802
- const id = React246.useId();
8803
- const stack = React246.useContext(StickyStackContext);
9303
+ const id = React247.useId();
9304
+ const stack = React247.useContext(StickyStackContext);
8804
9305
  const report = stack?.report;
8805
9306
  const stackSurface = stack?.surfaces.get(id);
8806
- const elementRef = React246.useRef(null);
9307
+ const elementRef = React247.useRef(null);
8807
9308
  const ref = useComposedRefs(forwardedRef, elementRef);
8808
- const [stuck, setStuck] = React246.useState(false);
8809
- const checkRef = React246.useRef(() => {
9309
+ const [stuck, setStuck] = React247.useState(false);
9310
+ const checkRef = React247.useRef(() => {
8810
9311
  });
8811
9312
  const notifyStuck = useCallbackRef(onStuckChange);
8812
- React246.useEffect(() => {
9313
+ React247.useEffect(() => {
8813
9314
  const element = elementRef.current;
8814
9315
  if (!element) return;
8815
9316
  const check = () => {
@@ -8832,8 +9333,8 @@ var StickyStackItem = React246.forwardRef(
8832
9333
  window.removeEventListener("resize", check);
8833
9334
  };
8834
9335
  }, []);
8835
- React246.useEffect(() => checkRef.current());
8836
- React246.useEffect(() => {
9336
+ React247.useEffect(() => checkRef.current());
9337
+ React247.useEffect(() => {
8837
9338
  notifyStuck(stuck);
8838
9339
  }, [stuck, notifyStuck]);
8839
9340
  useIsomorphicLayoutEffect(() => {
@@ -8875,7 +9376,7 @@ var ChipBar_default = {};
8875
9376
  var import_jsx_runtime244 = require("react/jsx-runtime");
8876
9377
  var OVERFLOW_BUTTON_SIZE = 32;
8877
9378
  var MASK_FADE_SIZE = 44;
8878
- var ChipBar = React247.forwardRef(
9379
+ var ChipBar = React248.forwardRef(
8879
9380
  ({
8880
9381
  leading,
8881
9382
  children,
@@ -8890,13 +9391,13 @@ var ChipBar = React247.forwardRef(
8890
9391
  }, ref) => {
8891
9392
  const surface = useBarSurface(surfaceProp);
8892
9393
  const sticky = useInStickyStackItem() ? false : stickyProp;
8893
- const [showLeftGradient, setShowLeftGradient] = React247.useState(false);
8894
- const [showRightGradient, setShowRightGradient] = React247.useState(false);
8895
- const [internalIsStuck, setInternalIsStuck] = React247.useState(false);
9394
+ const [showLeftGradient, setShowLeftGradient] = React248.useState(false);
9395
+ const [showRightGradient, setShowRightGradient] = React248.useState(false);
9396
+ const [internalIsStuck, setInternalIsStuck] = React248.useState(false);
8896
9397
  const isStuck = stuck !== void 0 ? stuck : internalIsStuck;
8897
- const listRef = React247.useRef(null);
8898
- const containerRef = React247.useRef(null);
8899
- const updateScrollGradients = React247.useCallback(() => {
9398
+ const listRef = React248.useRef(null);
9399
+ const containerRef = React248.useRef(null);
9400
+ const updateScrollGradients = React248.useCallback(() => {
8900
9401
  const el = listRef.current;
8901
9402
  if (el) {
8902
9403
  const { scrollLeft, scrollWidth, clientWidth } = el;
@@ -8904,7 +9405,7 @@ var ChipBar = React247.forwardRef(
8904
9405
  setShowRightGradient(Math.ceil(scrollLeft + clientWidth) < scrollWidth);
8905
9406
  }
8906
9407
  }, []);
8907
- React247.useEffect(() => {
9408
+ React248.useEffect(() => {
8908
9409
  const el = listRef.current;
8909
9410
  if (el) {
8910
9411
  updateScrollGradients();
@@ -8931,7 +9432,7 @@ var ChipBar = React247.forwardRef(
8931
9432
  });
8932
9433
  }
8933
9434
  };
8934
- React247.useEffect(() => {
9435
+ React248.useEffect(() => {
8935
9436
  if (!sticky || surface !== void 0) return;
8936
9437
  const container = containerRef.current;
8937
9438
  if (!container) return;
@@ -8963,7 +9464,7 @@ var ChipBar = React247.forwardRef(
8963
9464
  window.removeEventListener("resize", checkStuck);
8964
9465
  };
8965
9466
  }, [sticky, surface]);
8966
- const setRefs = React247.useCallback((node) => {
9467
+ const setRefs = React248.useCallback((node) => {
8967
9468
  containerRef.current = node;
8968
9469
  if (typeof ref === "function") {
8969
9470
  ref(node);
@@ -8999,7 +9500,7 @@ var ChipBar = React247.forwardRef(
8999
9500
  ...rest,
9000
9501
  children: [
9001
9502
  leading && /* @__PURE__ */ (0, import_jsx_runtime244.jsxs)("div", { className: ChipBar_default.chipBarLeading, children: [
9002
- React247.isValidElement(leading) ? React247.cloneElement(leading, {
9503
+ React248.isValidElement(leading) ? React248.cloneElement(leading, {
9003
9504
  size: size === "large" ? "large" : "medium"
9004
9505
  }) : leading,
9005
9506
  showDivider && /* @__PURE__ */ (0, import_jsx_runtime244.jsx)("div", { className: ChipBar_default.chipBarDivider })
@@ -9039,9 +9540,9 @@ var ChipBar = React247.forwardRef(
9039
9540
  "scrollbarHidden",
9040
9541
  "phx-chip-bar-list"
9041
9542
  ].filter(Boolean).join(" "),
9042
- children: React247.Children.map(children, (child) => {
9043
- if (React247.isValidElement(child)) {
9044
- return React247.cloneElement(child, {
9543
+ children: React248.Children.map(children, (child) => {
9544
+ if (React248.isValidElement(child)) {
9545
+ return React248.cloneElement(child, {
9045
9546
  size: size === "large" ? "large" : "medium"
9046
9547
  });
9047
9548
  }
@@ -9082,9 +9583,9 @@ var ChipBar = React247.forwardRef(
9082
9583
  ChipBar.displayName = "ChipBar";
9083
9584
 
9084
9585
  // ../ultimate/src/components/visually-hidden/VisuallyHidden.tsx
9085
- var React248 = __toESM(require("react"));
9586
+ var React249 = __toESM(require("react"));
9086
9587
  var import_jsx_runtime245 = require("react/jsx-runtime");
9087
- var VisuallyHidden = React248.forwardRef(
9588
+ var VisuallyHidden = React249.forwardRef(
9088
9589
  ({ as, children, className, style, ...props }, ref) => {
9089
9590
  const Component = as || "span";
9090
9591
  return /* @__PURE__ */ (0, import_jsx_runtime245.jsx)(
@@ -9114,26 +9615,150 @@ var VisuallyHidden = React248.forwardRef(
9114
9615
  VisuallyHidden.displayName = "VisuallyHidden";
9115
9616
 
9116
9617
  // ../ultimate/src/components/layout-context/LayoutContext.tsx
9117
- var React249 = __toESM(require("react"));
9618
+ var React251 = __toESM(require("react"));
9619
+
9620
+ // ../ultimate/src/components/layout-context/shell.ts
9621
+ var React250 = __toESM(require("react"));
9622
+ var ShellStore = class {
9623
+ state = {
9624
+ drawerFloating: false,
9625
+ drawerCount: 0,
9626
+ open: false,
9627
+ parentId: null,
9628
+ selectedRailId: null,
9629
+ noDrawer: /* @__PURE__ */ new Set()
9630
+ };
9631
+ listeners = /* @__PURE__ */ new Set();
9632
+ railClicks = /* @__PURE__ */ new Map();
9633
+ closeTimer;
9634
+ floatingDrawers = 0;
9635
+ /** Delay (ms) before the card closes, so the pointer can cross from the rail item to it. */
9636
+ closeDelay = 200;
9637
+ subscribe = (listener) => {
9638
+ this.listeners.add(listener);
9639
+ return () => {
9640
+ this.listeners.delete(listener);
9641
+ };
9642
+ };
9643
+ getSnapshot = () => this.state;
9644
+ set(patch) {
9645
+ this.state = { ...this.state, ...patch };
9646
+ this.listeners.forEach((listener) => listener());
9647
+ }
9648
+ reconcileQueued = false;
9649
+ /**
9650
+ * After a commit (rail items and drawers report in effects, in tree order), note whether the
9651
+ * selected item's page has a drawer.
9652
+ */
9653
+ reconcile() {
9654
+ if (this.reconcileQueued) return;
9655
+ this.reconcileQueued = true;
9656
+ queueMicrotask(() => {
9657
+ this.reconcileQueued = false;
9658
+ const { selectedRailId: id, drawerCount, noDrawer } = this.state;
9659
+ if (!id) return;
9660
+ const without = drawerCount === 0;
9661
+ if (without === noDrawer.has(id)) return;
9662
+ const next = new Set(noDrawer);
9663
+ if (without) next.add(id);
9664
+ else next.delete(id);
9665
+ this.set({ noDrawer: next });
9666
+ });
9667
+ }
9668
+ /** A drawer joins (`+1`) or leaves (`-1`) the layout. */
9669
+ drawerMounted(delta) {
9670
+ this.set({ drawerCount: Math.max(0, this.state.drawerCount + delta) });
9671
+ this.reconcile();
9672
+ if (this.state.drawerCount === 0) this.close();
9673
+ }
9674
+ /** A drawer starts (`true`) or stops floating. */
9675
+ drawerFloats(floating) {
9676
+ this.floatingDrawers = Math.max(0, this.floatingDrawers + (floating ? 1 : -1));
9677
+ const drawerFloating = this.floatingDrawers > 0;
9678
+ if (drawerFloating !== this.state.drawerFloating) this.set({ drawerFloating });
9679
+ if (!drawerFloating) this.close();
9680
+ }
9681
+ /** A rail item reports its click handler; the selected one also marks itself. */
9682
+ railItem(id, selected, onClick) {
9683
+ if (onClick) this.railClicks.set(id, onClick);
9684
+ else this.railClicks.delete(id);
9685
+ if (selected && this.state.selectedRailId !== id) this.set({ selectedRailId: id });
9686
+ if (selected) this.reconcile();
9687
+ return () => {
9688
+ if (this.railClicks.get(id) === onClick) this.railClicks.delete(id);
9689
+ };
9690
+ }
9691
+ /**
9692
+ * Whether a rail item reveals the floating drawer: any item, while a drawer floats in the
9693
+ * shell, except one whose page turned out to have none. `hint` (NavItem `hasDrawer`) decides
9694
+ * outright when given.
9695
+ */
9696
+ isParent(id, hint) {
9697
+ const { drawerFloating, drawerCount, noDrawer } = this.state;
9698
+ if (!drawerFloating || drawerCount === 0) return false;
9699
+ return hint ?? !noDrawer.has(id);
9700
+ }
9701
+ /** Opens at once; closing waits `closeDelay` and any open on the way cancels it. */
9702
+ setOpen = (open) => {
9703
+ clearTimeout(this.closeTimer);
9704
+ if (open) {
9705
+ if (!this.state.open) this.set({ open: true });
9706
+ } else {
9707
+ this.closeTimer = setTimeout(() => {
9708
+ if (this.state.open) this.set({ open: false });
9709
+ }, this.closeDelay);
9710
+ }
9711
+ };
9712
+ /** Pointer or focus entering / leaving a parent rail item. */
9713
+ hoverParent(id, hovering) {
9714
+ if (hovering && this.state.parentId !== id) this.set({ parentId: id });
9715
+ this.setOpen(hovering);
9716
+ }
9717
+ /** The app's own drawer toggle, while floating: show the card for the selected item, or hide it. */
9718
+ toggleForSelected() {
9719
+ if (this.state.open) {
9720
+ this.close();
9721
+ return;
9722
+ }
9723
+ clearTimeout(this.closeTimer);
9724
+ this.set({ open: true, parentId: this.state.selectedRailId });
9725
+ }
9726
+ close = () => {
9727
+ clearTimeout(this.closeTimer);
9728
+ if (this.state.open) this.set({ open: false });
9729
+ };
9730
+ /** An item was chosen in the floating card: go to the page it was opened from, then close. */
9731
+ choose() {
9732
+ const { parentId, selectedRailId, open } = this.state;
9733
+ if (!open) return;
9734
+ if (parentId && parentId !== selectedRailId) this.railClicks.get(parentId)?.(parentId);
9735
+ this.close();
9736
+ }
9737
+ };
9738
+ var defaultShellStore = new ShellStore();
9739
+
9740
+ // ../ultimate/src/components/layout-context/LayoutContext.tsx
9118
9741
  var import_jsx_runtime246 = require("react/jsx-runtime");
9119
- var LayoutContext = React249.createContext(null);
9742
+ var LayoutContext = React251.createContext(null);
9120
9743
  var LayoutProvider = ({ children }) => {
9121
- const [drawerCount, setDrawerCount] = React249.useState(0);
9122
- const registerDrawer = React249.useCallback(() => setDrawerCount((count) => count + 1), []);
9123
- const unregisterDrawer = React249.useCallback(() => setDrawerCount((count) => Math.max(0, count - 1)), []);
9124
- const value = React249.useMemo(() => ({
9744
+ const [drawerCount, setDrawerCount] = React251.useState(0);
9745
+ const [shell] = React251.useState(() => new ShellStore());
9746
+ const registerDrawer = React251.useCallback(() => setDrawerCount((count) => count + 1), []);
9747
+ const unregisterDrawer = React251.useCallback(() => setDrawerCount((count) => Math.max(0, count - 1)), []);
9748
+ const value = React251.useMemo(() => ({
9125
9749
  hasDrawer: drawerCount > 0,
9126
9750
  registerDrawer,
9127
- unregisterDrawer
9128
- }), [drawerCount, registerDrawer, unregisterDrawer]);
9751
+ unregisterDrawer,
9752
+ shell
9753
+ }), [drawerCount, registerDrawer, unregisterDrawer, shell]);
9129
9754
  return /* @__PURE__ */ (0, import_jsx_runtime246.jsx)(LayoutContext.Provider, { value, children });
9130
9755
  };
9131
9756
  var useLayout = () => {
9132
- return React249.useContext(LayoutContext);
9757
+ return React251.useContext(LayoutContext);
9133
9758
  };
9134
9759
 
9135
9760
  // ../ultimate/src/components/number-field/NumberField.tsx
9136
- var React250 = __toESM(require("react"));
9761
+ var React252 = __toESM(require("react"));
9137
9762
  var import_jsx_runtime247 = require("react/jsx-runtime");
9138
9763
  var parseNumber = (text) => {
9139
9764
  const normalized = text.trim().replace(",", ".");
@@ -9165,8 +9790,8 @@ function NumberField({
9165
9790
  defaultValue,
9166
9791
  onChange
9167
9792
  });
9168
- const ref = React250.useRef(null);
9169
- const [draft, setDraft] = React250.useState(null);
9793
+ const ref = React252.useRef(null);
9794
+ const [draft, setDraft] = React252.useState(null);
9170
9795
  const display = draft ?? (current == null ? "" : String(current));
9171
9796
  const resetValue = String((value !== void 0 ? current : defaultValue) ?? "");
9172
9797
  useNativeFieldReset(
@@ -9196,7 +9821,7 @@ function NumberField({
9196
9821
  )
9197
9822
  );
9198
9823
  };
9199
- const generatedId = React250.useId();
9824
+ const generatedId = React252.useId();
9200
9825
  const inputId = props.id ?? generatedId;
9201
9826
  const descriptionId = description ? `${inputId}-description` : void 0;
9202
9827
  const errorId = error ? `${inputId}-error` : void 0;
@@ -9314,17 +9939,17 @@ function NumberField({
9314
9939
  }
9315
9940
 
9316
9941
  // ../ultimate/src/components/rating/Rating.tsx
9317
- var React252 = __toESM(require("react"));
9942
+ var React254 = __toESM(require("react"));
9318
9943
 
9319
9944
  // ../ultimate/src/components/star/Star.tsx
9320
- var React251 = __toESM(require("react"));
9945
+ var React253 = __toESM(require("react"));
9321
9946
 
9322
9947
  // ../ultimate/src/components/star/Star.module.css
9323
9948
  var Star_default = {};
9324
9949
 
9325
9950
  // ../ultimate/src/components/star/Star.tsx
9326
9951
  var import_jsx_runtime248 = require("react/jsx-runtime");
9327
- var Star = React251.forwardRef(
9952
+ var Star = React253.forwardRef(
9328
9953
  ({
9329
9954
  className,
9330
9955
  checked = false,
@@ -9335,8 +9960,8 @@ var Star = React251.forwardRef(
9335
9960
  onClick,
9336
9961
  ...rest
9337
9962
  }, ref) => {
9338
- const [pressed, setPressed] = React251.useState(false);
9339
- const timeoutRef = React251.useRef(null);
9963
+ const [pressed, setPressed] = React253.useState(false);
9964
+ const timeoutRef = React253.useRef(null);
9340
9965
  const iconSize = size === "small" ? 20 : 24;
9341
9966
  const handleClick = (e) => {
9342
9967
  if (disabled) return;
@@ -9350,7 +9975,7 @@ var Star = React251.forwardRef(
9350
9975
  onClick(e);
9351
9976
  }
9352
9977
  };
9353
- React251.useEffect(() => {
9978
+ React253.useEffect(() => {
9354
9979
  return () => {
9355
9980
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
9356
9981
  };
@@ -9405,7 +10030,7 @@ function Rating({
9405
10030
  defaultValue,
9406
10031
  onChange
9407
10032
  });
9408
- const hidden = React252.useRef(null);
10033
+ const hidden = React254.useRef(null);
9409
10034
  const normalize = (rating) => Number.isFinite(rating) ? Math.max(0, Math.min(count, Math.round(rating))) : 0;
9410
10035
  const selected = normalize(current);
9411
10036
  useNativeFieldReset(
@@ -9486,7 +10111,7 @@ function Rating({
9486
10111
  }
9487
10112
 
9488
10113
  // ../ultimate/src/components/otp/OTP.tsx
9489
- var React253 = __toESM(require("react"));
10114
+ var React255 = __toESM(require("react"));
9490
10115
  var import_react_dom3 = require("react-dom");
9491
10116
 
9492
10117
  // ../ultimate/src/components/otp/OTP.module.css
@@ -9525,27 +10150,27 @@ function OTP({
9525
10150
  defaultValue: normalize(defaultValue),
9526
10151
  onChange
9527
10152
  });
9528
- const [local, setLocal] = React253.useState(() => split(value ?? defaultValue));
9529
- const pending = React253.useRef(null);
10153
+ const [local, setLocal] = React255.useState(() => split(value ?? defaultValue));
10154
+ const pending = React255.useRef(null);
9530
10155
  const digits = value === void 0 ? Array.from({ length: count }, (_, index) => local[index] ?? "") : pending.current?.code === normalize(code) ? Array.from(
9531
10156
  { length: count },
9532
10157
  (_, index) => pending.current.digits[index] ?? ""
9533
10158
  ) : split(code);
9534
- const currentDigits = React253.useRef(digits);
10159
+ const currentDigits = React255.useRef(digits);
9535
10160
  currentDigits.current = digits;
9536
- const composing = React253.useRef(null);
9537
- const picked = React253.useRef(false);
9538
- const inputs = React253.useRef([]);
9539
- const first = React253.useRef(null);
9540
- const previousReadOnly = React253.useRef(readOnly);
9541
- React253.useEffect(() => {
10161
+ const composing = React255.useRef(null);
10162
+ const picked = React255.useRef(false);
10163
+ const inputs = React255.useRef([]);
10164
+ const first = React255.useRef(null);
10165
+ const previousReadOnly = React255.useRef(readOnly);
10166
+ React255.useEffect(() => {
9542
10167
  if (readOnly && !previousReadOnly.current) {
9543
10168
  const active = inputs.current.find((input) => input === document.activeElement);
9544
10169
  active?.blur();
9545
10170
  }
9546
10171
  previousReadOnly.current = readOnly;
9547
10172
  }, [readOnly]);
9548
- const id = React253.useId();
10173
+ const id = React255.useId();
9549
10174
  const supportingText = error || helperText;
9550
10175
  const supportingId = error ? `${id}-error` : helperText ? `${id}-helper` : void 0;
9551
10176
  const focus = (index) => inputs.current[index]?.focus({ preventScroll: true });
@@ -9582,10 +10207,10 @@ function OTP({
9582
10207
  const nextIndex = sequential ? nextCell(next) : Math.min(count - 1, start + text.length);
9583
10208
  if (text && !inputs.current[nextIndex]?.readOnly) focus(nextIndex);
9584
10209
  };
9585
- React253.useEffect(() => {
10210
+ React255.useEffect(() => {
9586
10211
  if (autoFocus && !disabled && !readOnly) focus(sequential ? nextCell(currentDigits.current) : 0);
9587
10212
  }, []);
9588
- React253.useEffect(() => {
10213
+ React255.useEffect(() => {
9589
10214
  const cleanups = inputs.current.slice(0, count).map((input, index) => {
9590
10215
  if (!input) return () => {
9591
10216
  };
@@ -9643,7 +10268,7 @@ function OTP({
9643
10268
  gap: dotFields ? "md" : "4xs",
9644
10269
  maxW: "100%",
9645
10270
  w: `calc(${columns} * ${cellWidth}px + ${columns - 1 + separatorCount} * var(${dotFields ? "--space-md" : "--space-4xs"}) + ${separatorCount} * var(--dimensions-12))`,
9646
- children: Array.from({ length: count }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime250.jsxs)(React253.Fragment, { children: [
10271
+ children: Array.from({ length: count }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime250.jsxs)(React255.Fragment, { children: [
9647
10272
  separates(index % columns) && /* @__PURE__ */ (0, import_jsx_runtime250.jsx)(Text, { as: "span", "aria-hidden": "true", className: OTP_default.separator, children: "-" }),
9648
10273
  /* @__PURE__ */ (0, import_jsx_runtime250.jsxs)(Text, { as: "span", className: `${OTP_default.cellSlot} ${OTP_default[size]} ${dotFields ? OTP_default.dotField : ""}`, variant: size === "large" ? "headline-sm" : size === "medium" ? "title-lg" : "title-md", weight: "medium", children: [
9649
10274
  /* @__PURE__ */ (0, import_jsx_runtime250.jsx)(
@@ -9782,7 +10407,7 @@ function PIN({ length = 4, "aria-label": accessibleLabel = "PIN", autoFocus = tr
9782
10407
  }
9783
10408
 
9784
10409
  // ../ultimate/src/components/empty/Empty.tsx
9785
- var React254 = __toESM(require("react"));
10410
+ var React256 = __toESM(require("react"));
9786
10411
  var import_jsx_runtime252 = require("react/jsx-runtime");
9787
10412
  function Empty({
9788
10413
  title = "No results",
@@ -9790,7 +10415,7 @@ function Empty({
9790
10415
  icon,
9791
10416
  action
9792
10417
  }) {
9793
- const titleId = React254.useId();
10418
+ const titleId = React256.useId();
9794
10419
  return /* @__PURE__ */ (0, import_jsx_runtime252.jsxs)(
9795
10420
  VStack,
9796
10421
  {
@@ -9813,7 +10438,7 @@ function Empty({
9813
10438
  }
9814
10439
 
9815
10440
  // ../ultimate/src/components/result/Result.tsx
9816
- var React255 = __toESM(require("react"));
10441
+ var React257 = __toESM(require("react"));
9817
10442
  var import_illustrations = require("@photonix/illustrations");
9818
10443
  var import_jsx_runtime253 = require("react/jsx-runtime");
9819
10444
  var STATUS_ILLUSTRATION = {
@@ -9823,7 +10448,7 @@ var STATUS_ILLUSTRATION = {
9823
10448
  info: import_illustrations.Information
9824
10449
  };
9825
10450
  function Result({ status, title, description, action }) {
9826
- const titleId = React255.useId();
10451
+ const titleId = React257.useId();
9827
10452
  const StatusIllustration = STATUS_ILLUSTRATION[status];
9828
10453
  return /* @__PURE__ */ (0, import_jsx_runtime253.jsxs)(
9829
10454
  VStack,