@namphuongtechnologi/np-hub 0.2.0 → 0.2.1
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/{chunk-C5WUIBKX.js → chunk-CIUOZK3Q.js} +54 -13
- package/dist/chunk-CIUOZK3Q.js.map +1 -0
- package/dist/index.cjs +53 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/np-hub.min.global.js +5 -5
- package/dist/react.cjs +53 -12
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +5 -2
- package/dist/react.d.ts +5 -2
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/register.cjs +53 -12
- package/dist/register.cjs.map +1 -1
- package/dist/register.js +1 -1
- package/dist/{support-widget.element-BlSyAC7w.d.cts → support-widget.element-DewiQPzp.d.cts} +6 -2
- package/dist/{support-widget.element-BlSyAC7w.d.ts → support-widget.element-DewiQPzp.d.ts} +6 -2
- package/package.json +1 -1
- package/dist/chunk-C5WUIBKX.js.map +0 -1
|
@@ -167,7 +167,8 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
167
167
|
this.closeAfterSuccessTimer = null;
|
|
168
168
|
this.toastAutoCloseTimer = null;
|
|
169
169
|
this.toastCountdownInterval = null;
|
|
170
|
-
this.
|
|
170
|
+
this.toastSuccessDuration = 4e3;
|
|
171
|
+
this.toastErrorDuration = 4e3;
|
|
171
172
|
this.toastRemainingMs = 0;
|
|
172
173
|
this.toastTotalMs = 0;
|
|
173
174
|
this.isToastPaused = false;
|
|
@@ -233,7 +234,23 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
233
234
|
this.emailContacts = config.emailContacts;
|
|
234
235
|
}
|
|
235
236
|
if (config.toastDuration !== void 0) {
|
|
236
|
-
|
|
237
|
+
const duration = config.toastDuration;
|
|
238
|
+
if (typeof duration === "number") {
|
|
239
|
+
const val = this.normalizeToastDuration(duration);
|
|
240
|
+
this.toastSuccessDuration = val;
|
|
241
|
+
this.toastErrorDuration = val;
|
|
242
|
+
} else if (duration && typeof duration === "object") {
|
|
243
|
+
if (duration.success !== void 0) {
|
|
244
|
+
this.toastSuccessDuration = this.normalizeToastDuration(duration.success);
|
|
245
|
+
} else {
|
|
246
|
+
this.toastSuccessDuration = 4e3;
|
|
247
|
+
}
|
|
248
|
+
if (duration.error !== void 0) {
|
|
249
|
+
this.toastErrorDuration = this.normalizeToastDuration(duration.error);
|
|
250
|
+
} else {
|
|
251
|
+
this.toastErrorDuration = 4e3;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
237
254
|
}
|
|
238
255
|
}
|
|
239
256
|
setFormPrefill(data) {
|
|
@@ -251,6 +268,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
251
268
|
var _a, _b;
|
|
252
269
|
this.clearCloseAfterSuccessTimer();
|
|
253
270
|
this.resetFormData();
|
|
271
|
+
this.hideToast(false);
|
|
254
272
|
(_b = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("modal")) == null ? void 0 : _b.classList.remove("show");
|
|
255
273
|
this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.CLOSE, { bubbles: true }));
|
|
256
274
|
}
|
|
@@ -280,9 +298,27 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
280
298
|
};
|
|
281
299
|
const toastDurationAttr = this.getAttribute("toast-duration");
|
|
282
300
|
if (toastDurationAttr !== null) {
|
|
283
|
-
const
|
|
284
|
-
if (
|
|
285
|
-
|
|
301
|
+
const trimmed = toastDurationAttr.trim();
|
|
302
|
+
if (trimmed.startsWith("{")) {
|
|
303
|
+
try {
|
|
304
|
+
const parsed = JSON.parse(trimmed);
|
|
305
|
+
if (parsed && typeof parsed === "object") {
|
|
306
|
+
if (parsed.success !== void 0) {
|
|
307
|
+
this.toastSuccessDuration = this.normalizeToastDuration(Number(parsed.success));
|
|
308
|
+
}
|
|
309
|
+
if (parsed.error !== void 0) {
|
|
310
|
+
this.toastErrorDuration = this.normalizeToastDuration(Number(parsed.error));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
} catch (e) {
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
const parsed = parseInt(trimmed, 10);
|
|
317
|
+
if (!isNaN(parsed)) {
|
|
318
|
+
const val = this.normalizeToastDuration(parsed);
|
|
319
|
+
this.toastSuccessDuration = val;
|
|
320
|
+
this.toastErrorDuration = val;
|
|
321
|
+
}
|
|
286
322
|
}
|
|
287
323
|
}
|
|
288
324
|
}
|
|
@@ -723,7 +759,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
723
759
|
this.clearFieldError("email");
|
|
724
760
|
this.clearFieldError("content");
|
|
725
761
|
}
|
|
726
|
-
showToast(payload, autoCloseMs
|
|
762
|
+
showToast(payload, autoCloseMs) {
|
|
727
763
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
728
764
|
const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
|
|
729
765
|
const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
|
|
@@ -773,8 +809,9 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
773
809
|
linkRowEl.classList.add("is-hidden");
|
|
774
810
|
detailsEl.classList.add("is-hidden");
|
|
775
811
|
}
|
|
776
|
-
this.
|
|
777
|
-
this.
|
|
812
|
+
const duration = autoCloseMs !== void 0 ? autoCloseMs : payload.type === "success" ? this.toastSuccessDuration : this.toastErrorDuration;
|
|
813
|
+
this.toastRemainingMs = duration;
|
|
814
|
+
this.toastTotalMs = duration;
|
|
778
815
|
const toastCardEl = (_l = this.shadowRoot) == null ? void 0 : _l.querySelector(".toast-card");
|
|
779
816
|
this.isToastPaused = Boolean(toastCardEl == null ? void 0 : toastCardEl.matches(":hover"));
|
|
780
817
|
toastEl.classList.remove("success", "error");
|
|
@@ -785,7 +822,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
785
822
|
countdownBarEl
|
|
786
823
|
);
|
|
787
824
|
}
|
|
788
|
-
hideToast() {
|
|
825
|
+
hideToast(shouldCloseModal = true) {
|
|
789
826
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
790
827
|
const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
|
|
791
828
|
const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
|
|
@@ -806,6 +843,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
806
843
|
if (!toastEl || !titleEl || !messageEl || !detailsEl || !requestCodeEl || !linkRowEl || !linkEl || !countdownBarEl) {
|
|
807
844
|
return;
|
|
808
845
|
}
|
|
846
|
+
const isSuccess = toastEl.classList.contains("success");
|
|
809
847
|
this.clearToastAutoCloseTimer();
|
|
810
848
|
this.clearToastCountdown();
|
|
811
849
|
this.toastRemainingMs = 0;
|
|
@@ -827,6 +865,9 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
827
865
|
countdownEl.classList.add("is-hidden");
|
|
828
866
|
}
|
|
829
867
|
toastEl.classList.remove("success", "error", "show");
|
|
868
|
+
if (isSuccess && shouldCloseModal) {
|
|
869
|
+
this.close();
|
|
870
|
+
}
|
|
830
871
|
}
|
|
831
872
|
startToastCountdown(countdownEl, secondsEl, barEl) {
|
|
832
873
|
const updateCountdown = () => {
|
|
@@ -926,7 +967,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
926
967
|
async submit() {
|
|
927
968
|
var _a;
|
|
928
969
|
this.clearCloseAfterSuccessTimer();
|
|
929
|
-
this.hideToast();
|
|
970
|
+
this.hideToast(false);
|
|
930
971
|
if (!this.validateRequiredFields()) {
|
|
931
972
|
this.dispatchEvent(
|
|
932
973
|
new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {
|
|
@@ -1016,8 +1057,8 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
1016
1057
|
})
|
|
1017
1058
|
);
|
|
1018
1059
|
this.closeAfterSuccessTimer = window.setTimeout(() => {
|
|
1019
|
-
this.
|
|
1020
|
-
},
|
|
1060
|
+
this.hideToast();
|
|
1061
|
+
}, this.toastSuccessDuration);
|
|
1021
1062
|
} catch (error) {
|
|
1022
1063
|
const errorContent = mapHttpClientError(error);
|
|
1023
1064
|
this.showToast(this.toToastPayload(errorContent));
|
|
@@ -1135,4 +1176,4 @@ export {
|
|
|
1135
1176
|
SupportWidgetElement,
|
|
1136
1177
|
registerSupportWidget
|
|
1137
1178
|
};
|
|
1138
|
-
//# sourceMappingURL=chunk-
|
|
1179
|
+
//# sourceMappingURL=chunk-CIUOZK3Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/config/endpoints.ts","../src/core/validation/request-validator.ts","../src/services/api/create-request-response.ts","../src/services/api/http-client.ts","../src/services/api/support-api.ts","../src/component/events/widget-events.ts","../src/component/support-widget.styles.css","../src/component/support-widget.template.html","../src/component/support-widget.element.ts","../src/register.ts"],"sourcesContent":["export const PROD_API_URL = \"https://namphuong-api.azurewebsites.net\";\nexport const DEV_API_URL = \"https://namphuong-api-dev.azurewebsites.net\";\n\nexport function resolveBaseUrl(input: { isDev?: boolean }): string {\n return input.isDev ? DEV_API_URL : PROD_API_URL;\n}\n","import type {\n SupportSubmissionInput,\n SupportUser,\n SupportWidgetConfig,\n} from \"../../types/public\";\n\nexport function validateWidgetConfig(config: SupportWidgetConfig): void {\n if (config.projectId !== undefined && config.projectId.trim().length === 0) {\n throw new Error(\"projectId cannot be empty.\");\n }\n}\n\nexport function validateUser(user: SupportUser): void {\n if (!user.name?.trim()) {\n throw new Error(\"user.name is required.\");\n }\n if (!user.email?.trim()) {\n throw new Error(\"user.email is required.\");\n }\n if (!user.phoneNumber?.trim()) {\n throw new Error(\"user.phoneNumber is required.\");\n }\n}\n\nexport function validateSubmission(input: SupportSubmissionInput): void {\n if (!input.content?.trim()) {\n throw new Error(\"Content is required.\");\n }\n}\n","export interface CreateRequestResponse {\n statusCode?: number;\n message?: string;\n totalRecord?: number;\n data?: {\n url?: string;\n requestCode?: string;\n } | null;\n}\n\nexport interface SubmitToastContent {\n type: \"success\" | \"error\";\n message: string;\n statusCode?: string;\n requestCode?: string;\n url?: string;\n}\n\nconst SUCCESS_FALLBACK_MESSAGE = \"Yêu cầu đã được tiếp nhận thành công.\";\nconst ERROR_FALLBACK_MESSAGE = \"Có lỗi xảy ra khi gửi yêu cầu.\";\n\nexport function isCreateRequestSuccess(\n result: CreateRequestResponse,\n): boolean {\n if (typeof result.statusCode === \"number\") {\n return result.statusCode >= 200 && result.statusCode < 300;\n }\n return true;\n}\n\nexport function mapCreateRequestSuccess(\n result: CreateRequestResponse,\n): SubmitToastContent {\n const statusCode =\n typeof result.statusCode === \"number\"\n ? String(result.statusCode)\n : undefined;\n const requestCode = result.data?.requestCode?.trim() ?? \"\";\n const url = result.data?.url?.trim() ?? \"\";\n\n return {\n type: \"success\",\n message: result.message?.trim() || SUCCESS_FALLBACK_MESSAGE,\n statusCode,\n requestCode: requestCode || undefined,\n url: url || undefined,\n };\n}\n\nexport function mapCreateRequestFailure(\n result: CreateRequestResponse,\n): SubmitToastContent {\n const statusCode =\n typeof result.statusCode === \"number\"\n ? String(result.statusCode)\n : undefined;\n\n return {\n type: \"error\",\n message: result.message?.trim() || ERROR_FALLBACK_MESSAGE,\n statusCode,\n };\n}\n\nexport function mapHttpClientError(error: unknown): SubmitToastContent {\n const fallbackMessage = ERROR_FALLBACK_MESSAGE;\n const rawMessage =\n error instanceof Error ? error.message : fallbackMessage;\n const matchedStatus = rawMessage.match(/Request failed \\((\\d+)\\):/);\n const httpStatus = matchedStatus ? matchedStatus[1] : \"\";\n const afterColon = rawMessage.match(/Request failed \\(\\d+\\):\\s*(.*)$/s);\n const payloadText = afterColon?.[1]?.trim() ?? \"\";\n\n let parsedMessage = \"\";\n let bodyStatus = \"\";\n if (payloadText.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(payloadText) as CreateRequestResponse;\n if (typeof parsed.statusCode === \"number\") {\n bodyStatus = String(parsed.statusCode);\n }\n if (typeof parsed.message === \"string\") {\n parsedMessage = parsed.message.trim();\n }\n } catch {\n // Ignore JSON parse error, fallback to message extraction below.\n }\n }\n\n if (!parsedMessage && payloadText) {\n parsedMessage = payloadText;\n }\n if (!parsedMessage) {\n parsedMessage = rawMessage || fallbackMessage;\n }\n\n return {\n type: \"error\",\n message: parsedMessage,\n statusCode: bodyStatus || httpStatus || undefined,\n };\n}\n","export async function postFormData<TResponse>(\n url: string,\n body: FormData,\n): Promise<TResponse> {\n const response = await fetch(url, {\n method: \"POST\",\n body,\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(`Request failed (${response.status}): ${message}`);\n }\n\n return (await response.json()) as TResponse;\n}\n","import type { CreateRequestResponse } from \"./create-request-response\";\nimport { postFormData } from \"./http-client\";\n\nexport type { CreateRequestResponse };\n\nexport async function createSupportRequest(\n baseUrl: string,\n formData: FormData,\n): Promise<CreateRequestResponse> {\n const endpoint = `${baseUrl.replace(/\\/$/, \"\")}/api/supportcenter/create-request-anonymous`;\n return postFormData<CreateRequestResponse>(endpoint, formData);\n}\n\n","export const WIDGET_EVENTS = {\n OPEN: \"np-hub-open\",\n CLOSE: \"np-hub-close\",\n SUBMIT_SUCCESS: \"np-hub-submit-success\",\n SUBMIT_ERROR: \"np-hub-submit-error\",\n} as const;\n","@import url(\"https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap\");\n\n:host {\n --np-primary: #003c82;\n --np-primary-dark: #002a5c;\n --np-primary-soft: rgba(0, 60, 130, 0.08);\n --np-border: #e2e8f0;\n --np-text: #0f172a;\n --np-text-muted: #64748b;\n font-family: \"Outfit\", sans-serif;\n display: block;\n position: fixed;\n right: var(--np-hub-right, 20px);\n bottom: var(--np-hub-bottom, 20px);\n left: var(--np-hub-left, auto);\n top: var(--np-hub-top, auto);\n z-index: 10000;\n}\n\n.launcher {\n width: var(--np-hub-width, 65px);\n height: var(--np-hub-height, 65px);\n border: none;\n border-radius: 50%;\n cursor: grab;\n box-shadow: 0 8px 28px rgba(6, 6, 7, 0.2);\n background-color: #ffffff;\n background-image: var(--np-hub-icon);\n background-position: center;\n background-repeat: no-repeat;\n background-size: 75%;\n transition:\n transform 0.2s ease,\n box-shadow 0.2s ease;\n}\n\n.launcher:hover {\n transform: scale(1.05);\n box-shadow: 0 12px 32px rgba(0, 60, 130, 0.3);\n}\n\n.launcher:active {\n cursor: grabbing;\n}\n\n.modal {\n position: fixed;\n inset: 0;\n background: rgba(15, 23, 42, 0.55);\n backdrop-filter: blur(6px);\n display: none;\n align-items: center;\n justify-content: center;\n padding: 16px;\n opacity: 0;\n transition: opacity 0.25s ease-out;\n}\n\n.modal.show {\n display: flex;\n opacity: 1;\n}\n\n.dialog {\n width: min(540px, 100%);\n max-height: calc(100vh - 32px);\n overflow-y: auto;\n background: #ffffff;\n border-radius: 20px;\n box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);\n padding: 28px 28px 26px;\n box-sizing: border-box;\n position: relative;\n transform: translateY(24px) scale(0.98);\n transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.modal.show .dialog {\n transform: translateY(0) scale(1);\n}\n\n.dialog::-webkit-scrollbar {\n width: 8px;\n}\n\n.dialog::-webkit-scrollbar-thumb {\n background: #cbd5e1;\n border-radius: 8px;\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n padding-bottom: 12px;\n margin-bottom: 16px;\n border-bottom: 1px solid var(--np-border);\n position: relative;\n}\n\n.title {\n margin: 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--np-primary);\n text-align: center;\n letter-spacing: -0.01em;\n}\n\n.close-btn {\n border: none;\n background: #f1f5f9;\n color: #64748b;\n font-size: 20px;\n line-height: 1;\n cursor: pointer;\n position: absolute;\n right: 0;\n top: 0;\n bottom: 18px;\n margin: auto 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n\n.close-btn:hover {\n background: #e2e8f0;\n color: #0f172a;\n transform: rotate(90deg);\n}\n\n.form-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 20px;\n}\n\n.field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.field-error {\n display: none;\n}\n\n.field.full {\n grid-column: 1 / -1;\n}\n\nlabel {\n font-size: 13px;\n font-weight: 600;\n color: #334155;\n}\n\n.required {\n color: #ef4444;\n margin-right: 3px;\n}\n\ninput,\ntextarea {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #d5dde7;\n border-radius: 10px;\n font-size: 14px;\n padding: 11px 13px;\n color: var(--np-text);\n outline: none;\n font-family: inherit;\n background: #fbfcfe;\n transition:\n border-color 0.15s ease,\n box-shadow 0.15s ease,\n background 0.15s ease;\n}\n\ntextarea {\n min-height: 100px;\n resize: vertical;\n line-height: 1.5;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n color: #94a3b8;\n}\n\ninput:hover,\ntextarea:hover {\n border-color: #b6c2d1;\n}\n\ninput:focus,\ntextarea:focus {\n border-color: var(--np-primary);\n background: #ffffff;\n box-shadow: 0 0 0 4px var(--np-primary-soft);\n}\n\n.input-wrap.has-error input,\n.input-wrap.has-error textarea {\n border-color: #e45b5b;\n background: #ffffff;\n}\n\n.input-wrap.has-error input:focus,\n.input-wrap.has-error textarea:focus {\n border-color: #e45b5b;\n box-shadow: 0 0 0 3px rgba(228, 91, 91, 0.14);\n}\n\n.input-wrap {\n position: relative;\n}\n\n.input-wrap input,\n.input-wrap textarea {\n padding-right: 40px;\n}\n\n.btn-clear {\n position: absolute;\n right: 10px;\n top: 50%;\n transform: translateY(-50%);\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 50%;\n background: #e2e8f0;\n color: #64748b;\n font-size: 16px;\n line-height: 1;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n opacity: 0;\n pointer-events: none;\n transition:\n opacity 0.15s ease,\n background 0.15s ease,\n color 0.15s ease;\n}\n\n.input-wrap.has-value:hover .btn-clear,\n.input-wrap.has-value:focus-within .btn-clear {\n opacity: 1;\n pointer-events: auto;\n}\n\n.btn-clear:hover {\n background: #cbd5e1;\n color: #0f172a;\n}\n\n.input-wrap--textarea .btn-clear {\n top: 12px;\n transform: none;\n}\n\n/* File upload styling */\n.upload-zone {\n display: flex;\n align-items: center;\n gap: 16px;\n background: #f8fafc;\n border: 1.5px dashed #cbd5e1;\n padding: 16px 16px;\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n user-select: none;\n outline: none;\n}\n\n.upload-zone:hover {\n background: var(--np-primary-soft);\n border-color: var(--np-primary);\n}\n\n.upload-zone:focus-visible {\n border-color: var(--np-primary);\n box-shadow: 0 0 0 4px var(--np-primary-soft);\n}\n\n.upload-zone.dragover {\n background: rgba(0, 60, 130, 0.08);\n border-color: var(--np-primary);\n transform: scale(1.005);\n}\n\n.upload-icon-wrapper {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 10px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n color: #64748b;\n box-shadow: 0 2px 4px rgba(15, 23, 42, 0.04);\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.upload-zone:hover .upload-icon-wrapper,\n.upload-zone.dragover .upload-icon-wrapper {\n color: var(--np-primary);\n border-color: rgba(0, 60, 130, 0.15);\n box-shadow: 0 4px 8px rgba(0, 60, 130, 0.08);\n}\n\n.upload-text-container {\n display: flex;\n flex-direction: column;\n gap: 3px;\n flex: 1;\n}\n\n.upload-main-text {\n font-size: 13px;\n font-weight: 600;\n color: #334155;\n transition: color 0.2s ease;\n}\n\n.upload-zone:hover .upload-main-text {\n color: var(--np-primary);\n}\n\n.upload-sub-text {\n font-size: 11px;\n color: #64748b;\n line-height: 1.4;\n}\n\n.file-list {\n margin-top: 8px;\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\n gap: 12px;\n}\n\n.file-item {\n position: relative;\n display: flex;\n flex-direction: column;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 12px;\n font-size: 11px;\n color: #475569;\n box-shadow: 0 2px 5px rgba(15, 23, 42, 0.03);\n animation: file-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n transition: all 0.2s ease;\n overflow: hidden;\n}\n\n.file-item:hover {\n border-color: #cbd5e1;\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.08);\n transform: translateY(-2px);\n}\n\n.file-preview-trigger {\n border: none;\n background: none;\n padding: 0;\n margin: 0;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n width: 100%;\n color: inherit;\n font: inherit;\n}\n\n.file-thumb {\n width: 100%;\n aspect-ratio: 1 / 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f8fafc;\n border-bottom: 1px solid #f1f5f9;\n position: relative;\n overflow: hidden;\n transition: background 0.2s ease;\n}\n\n.file-thumb img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n transition: transform 0.25s ease;\n}\n\n.file-item:hover .file-thumb img {\n transform: scale(1.06);\n}\n\n.file-thumb--icon {\n color: #64748b;\n background: #f1f5f9;\n}\n\n.file-item:hover .file-thumb--icon {\n background: #e2e8f0;\n color: var(--np-primary);\n}\n\n@keyframes file-in {\n from {\n opacity: 0;\n transform: translateY(6px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.file-info-container {\n padding: 8px 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n box-sizing: border-box;\n}\n\n.file-item .file-name {\n width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n line-height: 1.2;\n color: #334155;\n font-size: 11px;\n font-weight: 500;\n text-align: center;\n}\n\n.file-badge {\n position: absolute;\n top: 6px;\n left: 6px;\n z-index: 2;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 9px;\n font-weight: 600;\n line-height: 1;\n color: #0369a1;\n background: rgba(224, 242, 254, 0.9);\n border: 1px solid rgba(186, 230, 253, 0.8);\n backdrop-filter: blur(4px);\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);\n}\n\n.file-item .btn-remove {\n position: absolute;\n top: 6px;\n right: 6px;\n z-index: 2;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(255, 255, 255, 0.9);\n border: 1px solid #e2e8f0;\n border-radius: 50%;\n color: #64748b;\n cursor: pointer;\n padding: 0;\n box-shadow: 0 2px 4px rgba(15, 23, 42, 0.06);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n backdrop-filter: blur(4px);\n}\n\n.file-item .btn-remove svg {\n width: 8px;\n height: 8px;\n stroke-width: 3px;\n transition: transform 0.2s ease;\n}\n\n.file-item .btn-remove:hover {\n color: #ffffff;\n background: #ef4444;\n border-color: #ef4444;\n transform: scale(1.1);\n box-shadow: 0 3px 8px rgba(239, 68, 68, 0.25);\n}\n\n.file-item .btn-remove:hover svg {\n transform: rotate(90deg);\n}\n\n/* File preview popup */\n.preview-overlay {\n position: fixed;\n inset: 0;\n z-index: 20;\n display: none;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: rgba(15, 23, 42, 0.72);\n backdrop-filter: blur(4px);\n opacity: 0;\n transition: opacity 0.2s ease-out;\n}\n\n.preview-overlay.show {\n display: flex;\n opacity: 1;\n}\n\n.preview-box {\n position: relative;\n max-width: min(720px, 100%);\n max-height: calc(100vh - 96px);\n background: #ffffff;\n border-radius: 16px;\n padding: 16px;\n box-shadow: 0 24px 60px rgba(15, 23, 42, 0.35);\n display: flex;\n flex-direction: column;\n}\n\n.preview-close {\n position: absolute;\n top: -14px;\n right: -14px;\n width: 34px;\n height: 34px;\n border-radius: 50%;\n border: none;\n background: #ffffff;\n color: #334155;\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.25);\n transition:\n color 0.15s ease,\n transform 0.15s ease;\n}\n\n.preview-close:hover {\n color: #ef4444;\n transform: scale(1.05);\n}\n\n.preview-body {\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.preview-body img {\n max-width: 100%;\n max-height: calc(100vh - 140px);\n object-fit: contain;\n border-radius: 8px;\n display: block;\n}\n\n.preview-file {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 32px 40px;\n text-align: center;\n color: var(--np-text);\n}\n\n.preview-file svg {\n color: var(--np-primary);\n}\n\n.preview-file .preview-file-name {\n font-size: 14px;\n font-weight: 600;\n word-break: break-all;\n}\n\n.preview-file a {\n color: var(--np-primary);\n font-size: 13px;\n font-weight: 600;\n text-decoration: none;\n}\n\n.preview-file a:hover {\n text-decoration: underline;\n}\n\n.actions {\n margin-top: 20px;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n}\n\n.toast {\n position: fixed;\n inset: 0;\n z-index: 10002;\n display: none;\n align-items: flex-start;\n justify-content: center;\n background: transparent;\n pointer-events: auto; /* disable interaction underneath */\n}\n\n.toast.show {\n display: flex;\n animation: toast-fade-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n}\n\n.toast.success .toast-card {\n --toast-status-color: #10b981;\n --toast-status-bg: rgba(16, 185, 129, 0.12);\n --toast-badge-text: #047857;\n --toast-badge-bg: #ecfdf5;\n --toast-badge-border: #d1fae5;\n}\n\n.toast.error .toast-card {\n --toast-status-color: #ef4444;\n --toast-status-bg: rgba(239, 68, 68, 0.12);\n --toast-badge-text: #b91c1c;\n --toast-badge-bg: #fef2f2;\n --toast-badge-border: #fee2e2;\n}\n\n.toast-card {\n position: relative;\n width: min(90vw, 420px);\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 20px 24px 22px 24px;\n border-radius: 20px; /* matched to main dialog border-radius */\n background: #ffffff;\n border: 1px solid var(--np-border);\n box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22); /* matched to main dialog shadow */\n pointer-events: auto;\n overflow: hidden;\n margin-top: 24px; /* spacing from top of viewport */\n animation: toast-in 0.35s cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.toast-main {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.toast-icon {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n background: var(--toast-status-bg);\n margin-top: 2px;\n}\n\n.toast-icon svg {\n display: none;\n width: 18px;\n height: 18px;\n margin: 0;\n}\n\n.toast.success .toast-icon svg.icon-success {\n display: block;\n color: var(--toast-status-color);\n}\n\n.toast.error .toast-icon svg.icon-error {\n display: block;\n color: var(--toast-status-color);\n}\n\n.toast-body {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n min-width: 0;\n padding-top: 4px;\n}\n\n.toast-title {\n margin: 0;\n font-size: 16px;\n font-weight: 700;\n color: #111827; /* dark charcoal black */\n letter-spacing: -0.01em;\n line-height: 1.3;\n}\n\n.toast-message {\n margin: 0;\n font-size: 13.5px;\n line-height: 1.45;\n color: var(--np-text-muted);\n}\n\n.toast.success .toast-message {\n text-transform: uppercase;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.05em;\n color: var(--np-text-muted);\n margin-top: 2px;\n}\n\n.toast-close-wrapper {\n position: relative;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.toast-countdown-ring {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n}\n\n.ring-track {\n stroke: #f1f5f9; /* soft gray track */\n}\n\n.ring-bar {\n stroke-linecap: round;\n transition: stroke-dashoffset 0.1s linear;\n}\n\n.toast-close-btn {\n position: relative;\n z-index: 1;\n border: none;\n background: transparent;\n color: #94a3b8; /* subtle gray close icon */\n width: 28px;\n height: 28px;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n\n.toast-close-btn svg {\n width: 12px;\n height: 12px;\n display: block;\n}\n\n.toast-close-btn:hover {\n background: #f1f5f9;\n color: #0f172a;\n transform: rotate(90deg);\n}\n\n.toast-details {\n margin-left: 44px;\n margin-top: 4px;\n padding: 12px 0 0 0;\n border-top: 1px solid var(--np-border); /* separator line */\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.toast-detail-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.toast-detail-label {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: var(--np-text-muted);\n}\n\n.toast-detail-value {\n min-width: 0;\n font-size: 13px;\n line-height: 1.4;\n color: var(--np-text);\n display: flex;\n justify-content: flex-end;\n}\n\n.toast-detail-code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-weight: 600;\n color: var(--np-primary);\n background: var(--np-primary-soft);\n border: 1px solid rgba(0, 60, 130, 0.15);\n padding: 2px 8px;\n border-radius: 8px;\n font-size: 12px;\n display: inline-block;\n text-align: right;\n}\n\n.toast-detail-link {\n color: var(--np-primary);\n text-decoration: none;\n font-weight: 600;\n display: inline-block;\n max-width: 220px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-bottom: 1px dashed rgba(0, 60, 130, 0.3);\n transition: all 0.15s ease;\n font-size: 12.5px;\n}\n\n.toast-detail-link:hover {\n color: var(--np-primary-dark);\n border-bottom-style: solid;\n border-bottom-color: var(--np-primary-dark);\n}\n\n.toast-detail-value-wrap {\n display: flex;\n align-items: center;\n gap: 6px;\n justify-content: flex-end;\n min-width: 0;\n}\n\n.toast-copy-btn {\n background: transparent;\n border: none;\n padding: 4px;\n margin: 0;\n cursor: pointer;\n color: var(--np-text-muted);\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n}\n\n.toast-copy-btn:hover {\n background: #f1f5f9;\n color: var(--np-primary);\n}\n\n.toast-copy-btn svg {\n width: 14px;\n height: 14px;\n display: block;\n}\n\n.toast-copy-btn .icon-copied {\n color: var(--np-primary);\n}\n\n.toast-copy-btn.copied {\n background: var(--np-primary-soft);\n}\n\n.toast-countdown {\n margin-left: 44px;\n margin-top: 14px;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.toast-countdown::before {\n content: \"\";\n flex: 1;\n height: 1px;\n background: #f1f5f9; /* soft divider line */\n}\n\n.toast-countdown-row {\n font-size: 11px;\n color: var(--np-text-muted);\n font-weight: 500;\n white-space: nowrap;\n display: inline-block;\n}\n\n.toast.success .toast-countdown-row {\n text-transform: none;\n font-size: 12px;\n}\n\n.toast.error .toast-countdown-row {\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.toast .is-hidden {\n display: none !important;\n}\n\n@keyframes toast-in {\n from {\n opacity: 0;\n transform: translateY(-20px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n@keyframes toast-fade-in {\n from {\n background: rgba(15, 23, 42, 0);\n backdrop-filter: blur(0px);\n -webkit-backdrop-filter: blur(0px);\n }\n to {\n background: rgba(15, 23, 42, 0.35);\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n }\n}\n\n.btn {\n border-radius: 10px;\n padding: 10px 22px;\n font-size: 15px;\n font-weight: 600;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.18s ease;\n min-width: 120px;\n}\n\n.btn:active {\n transform: translateY(1px);\n}\n\n.btn-secondary {\n background: #ffffff;\n border: 1px solid #d5dde7;\n color: #334155;\n}\n\n.btn-secondary:hover {\n background: #f1f5f9;\n border-color: #b6c2d1;\n}\n\n.btn-primary {\n background: linear-gradient(\n 135deg,\n var(--np-primary),\n var(--np-primary-dark)\n );\n border: 1px solid var(--np-primary-dark);\n color: #ffffff;\n box-shadow: 0 6px 16px rgba(0, 60, 130, 0.28);\n}\n\n.btn-primary:hover {\n box-shadow: 0 8px 22px rgba(0, 60, 130, 0.38);\n filter: brightness(1.06);\n}\n\n@media (max-width: 480px) {\n .dialog {\n padding: 24px 20px 24px;\n border-radius: 16px;\n }\n\n .form-grid {\n grid-template-columns: 1fr;\n }\n\n .actions {\n justify-content: stretch;\n }\n\n .actions .btn {\n flex: 1;\n }\n}\n","<button\n id=\"open-btn\"\n class=\"launcher\"\n type=\"button\"\n aria-label=\"Open support form\"\n></button>\n\n<div id=\"modal\" class=\"modal\">\n <div\n class=\"dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Support request form\"\n >\n <div class=\"header\">\n <h3 class=\"title\">Tạo yêu cầu hỗ trợ</h3>\n <button id=\"close-btn\" class=\"close-btn\" type=\"button\" aria-label=\"Close\">\n ×\n </button>\n </div>\n\n <div class=\"form-grid\">\n <div class=\"field\">\n <label for=\"requester\"><span class=\"required\">*</span> Họ và tên</label>\n <div class=\"input-wrap\">\n <input id=\"requester\" type=\"text\" placeholder=\"Họ và tên\" />\n <button type=\"button\" class=\"btn-clear\" aria-label=\"Xóa\">×</button>\n </div>\n <p id=\"requester-error\" class=\"field-error\" aria-live=\"polite\"></p>\n </div>\n\n <div class=\"field\">\n <label for=\"phone-number\"\n ><span class=\"required\">*</span> Số điện thoại</label\n >\n <div class=\"input-wrap\">\n <input id=\"phone-number\" type=\"text\" placeholder=\"Số điện thoại\" />\n <button type=\"button\" class=\"btn-clear\" aria-label=\"Xóa\">×</button>\n </div>\n <p id=\"phone-number-error\" class=\"field-error\" aria-live=\"polite\"></p>\n </div>\n\n <div class=\"field full\">\n <label for=\"email\"><span class=\"required\">*</span> Email</label>\n <div class=\"input-wrap\">\n <input id=\"email\" type=\"email\" placeholder=\"Email\" />\n <button type=\"button\" class=\"btn-clear\" aria-label=\"Xóa\">×</button>\n </div>\n <p id=\"email-error\" class=\"field-error\" aria-live=\"polite\"></p>\n </div>\n\n <div class=\"field full\">\n <label for=\"content\"><span class=\"required\">*</span> Nội dung</label>\n <div class=\"input-wrap input-wrap--textarea\">\n <textarea\n id=\"content\"\n placeholder=\"Anh/Chị vui lòng nhập thông tin cần hỗ trợ\"\n ></textarea>\n <button type=\"button\" class=\"btn-clear\" aria-label=\"Xóa\">×</button>\n </div>\n <p id=\"content-error\" class=\"field-error\" aria-live=\"polite\"></p>\n </div>\n\n <div class=\"field full\">\n <label>File đính kèm</label>\n <div\n id=\"upload-trigger\"\n class=\"upload-zone\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Tải lên tệp\"\n >\n <input id=\"file-input\" type=\"file\" multiple style=\"display: none\" />\n <div class=\"upload-icon-wrapper\">\n <svg\n class=\"upload-icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"></path>\n <polyline points=\"17 8 12 3 7 8\"></polyline>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"></line>\n </svg>\n </div>\n <div class=\"upload-text-container\">\n <span class=\"upload-main-text\"\n >Chọn tệp tải lên hoặc kéo thả vào đây</span\n >\n <span class=\"upload-sub-text\"\n >Hỗ trợ dán ảnh hoặc tệp (Ctrl+V / Cmd+V) để đính kèm nhanh</span\n >\n </div>\n </div>\n <div id=\"file-list\" class=\"file-list\"></div>\n </div>\n </div>\n\n <div id=\"preview-overlay\" class=\"preview-overlay\">\n <div\n class=\"preview-box\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Xem trước tệp\"\n >\n <button\n id=\"preview-close\"\n class=\"preview-close\"\n type=\"button\"\n aria-label=\"Đóng\"\n >\n ×\n </button>\n <div id=\"preview-body\" class=\"preview-body\"></div>\n </div>\n </div>\n\n <div class=\"actions\">\n <button id=\"cancel-btn\" class=\"btn btn-secondary\" type=\"button\">\n Hủy\n </button>\n <button id=\"submit-btn\" class=\"btn btn-primary\" type=\"button\">\n Tạo yêu cầu\n </button>\n </div>\n </div>\n</div>\n\n<div\n id=\"toast\"\n class=\"toast\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n>\n <div class=\"toast-card\">\n <div class=\"toast-main\">\n <div class=\"toast-icon\" aria-hidden=\"true\">\n <svg\n class=\"icon-success\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n <svg\n class=\"icon-error\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"></line>\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"></line>\n </svg>\n </div>\n <div class=\"toast-body\">\n <h4 id=\"toast-title\" class=\"toast-title\"></h4>\n <p id=\"toast-message\" class=\"toast-message\"></p>\n </div>\n <div class=\"toast-close-wrapper\">\n <svg\n class=\"toast-countdown-ring\"\n width=\"36\"\n height=\"36\"\n viewBox=\"0 0 36 36\"\n >\n <circle\n class=\"ring-track\"\n cx=\"18\"\n cy=\"18\"\n r=\"15\"\n fill=\"none\"\n stroke=\"#e2e8f0\"\n stroke-width=\"2.5\"\n ></circle>\n <circle\n id=\"toast-countdown-bar\"\n class=\"ring-bar\"\n cx=\"18\"\n cy=\"18\"\n r=\"15\"\n fill=\"none\"\n stroke=\"var(--toast-status-color)\"\n stroke-width=\"2.5\"\n stroke-dasharray=\"94.25\"\n stroke-dashoffset=\"0\"\n transform=\"rotate(-90 18 18)\"\n ></circle>\n </svg>\n <button\n id=\"toast-close-btn\"\n class=\"toast-close-btn\"\n type=\"button\"\n aria-label=\"Đóng thông báo\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n </div>\n </div>\n <div id=\"toast-details\" class=\"toast-details is-hidden\">\n <div class=\"toast-detail-row\">\n <span class=\"toast-detail-label\">Mã hỗ trợ</span>\n <div class=\"toast-detail-value-wrap\">\n <span\n id=\"toast-request-code\"\n class=\"toast-detail-value toast-detail-code\"\n ></span>\n <button\n id=\"toast-copy-code-btn\"\n class=\"toast-copy-btn\"\n type=\"button\"\n title=\"Sao chép mã hỗ trợ\"\n >\n <svg\n class=\"icon-copy\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect>\n <path\n d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\n ></path>\n </svg>\n <svg\n class=\"icon-copied is-hidden\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n </button>\n </div>\n </div>\n <div id=\"toast-link-row\" class=\"toast-detail-row is-hidden\">\n <span class=\"toast-detail-label\">Tra cứu</span>\n <div class=\"toast-detail-value-wrap\">\n <a\n id=\"toast-link\"\n class=\"toast-detail-value toast-detail-link\"\n href=\"#\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n ></a>\n <button\n id=\"toast-copy-link-btn\"\n class=\"toast-copy-btn\"\n type=\"button\"\n title=\"Sao chép liên kết\"\n >\n <svg\n class=\"icon-copy\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect>\n <path\n d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\n ></path>\n </svg>\n <svg\n class=\"icon-copied is-hidden\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n </button>\n </div>\n </div>\n </div>\n </div>\n</div>\n","import { resolveBaseUrl } from \"../core/config/endpoints\";\nimport {\n validateSubmission,\n validateUser,\n validateWidgetConfig,\n} from \"../core/validation/request-validator\";\nimport {\n isCreateRequestSuccess,\n mapCreateRequestFailure,\n mapCreateRequestSuccess,\n mapHttpClientError,\n type SubmitToastContent,\n} from \"../services/api/create-request-response\";\nimport { createSupportRequest } from \"../services/api/support-api\";\nimport type {\n SupportSubmissionInput,\n SupportUser,\n SupportUserPrefill,\n SupportWidgetConfig,\n} from \"../types/public\";\nimport { WIDGET_EVENTS } from \"./events/widget-events\";\nimport npSupportLogo from \"./assets/np-support-logo.png\";\nimport styles from \"./support-widget.styles.css\";\nimport markup from \"./support-widget.template.html\";\n\nlet sharedTemplate: HTMLTemplateElement | null = null;\n\nfunction getTemplate(): HTMLTemplateElement {\n if (!sharedTemplate) {\n sharedTemplate = document.createElement(\"template\");\n sharedTemplate.innerHTML = `<style>${styles}</style>${markup}`;\n }\n return sharedTemplate;\n}\n\nasync function urlToFile(url: string): Promise<File | null> {\n try {\n const response = await fetch(url);\n if (!response.ok) return null;\n const blob = await response.blob();\n const name = url.substring(url.lastIndexOf(\"/\") + 1) || \"attachment\";\n return new File([blob], name, { type: blob.type });\n } catch (e) {\n console.warn(\"Failed to fetch prefilled attachment\", url, e);\n return null;\n }\n}\n\ninterface FormPrefillInput {\n content?: string;\n attachments?: string[];\n}\n\ninterface ToastPayload extends SubmitToastContent {\n title: string;\n}\n\ntype RequiredFieldId = \"requester\" | \"phone-number\" | \"email\" | \"content\";\n\nexport class SupportWidgetElement extends HTMLElement {\n static observedAttributes = [\n \"project-id\",\n \"is-dev\",\n \"width\",\n \"height\",\n \"right\",\n \"bottom\",\n \"left\",\n \"top\",\n \"toast-duration\",\n ];\n\n private config: SupportWidgetConfig = {\n projectId: undefined,\n };\n\n private user?: SupportUserPrefill;\n private dragOccurred = false;\n\n // Internal states for fields mapped behind the scenes\n private selectedFiles: File[] = [];\n private prefilledAttachments: string[] = [];\n private previewUrls: string[] = [];\n private closeAfterSuccessTimer: number | null = null;\n private toastAutoCloseTimer: number | null = null;\n private toastCountdownInterval: number | null = null;\n private toastSuccessDuration = 4000;\n private toastErrorDuration = 4000;\n private toastRemainingMs = 0;\n private toastTotalMs = 0;\n private isToastPaused = false;\n public priority: number | undefined;\n public coordinators: string[] = [];\n public emailContacts: string[] = [];\n\n constructor() {\n super();\n const root = this.attachShadow({ mode: \"open\" });\n root.appendChild(getTemplate().content.cloneNode(true));\n this.bindActions();\n }\n\n connectedCallback(): void {\n this.syncConfigFromAttributes();\n this.prefillFromState();\n this.applyLauncherStyle();\n this.applyPositionStyle();\n this.restorePosition();\n window.addEventListener(\"resize\", this.handleResize);\n }\n\n disconnectedCallback(): void {\n window.removeEventListener(\"resize\", this.handleResize);\n this.clearCloseAfterSuccessTimer();\n this.clearToastAutoCloseTimer();\n this.clearToastCountdown();\n }\n\n attributeChangedCallback(): void {\n this.syncConfigFromAttributes();\n this.prefillFromState();\n this.applyLauncherStyle();\n this.applyPositionStyle();\n }\n\n setUser(user: SupportUserPrefill): void {\n this.user = user;\n this.prefillFromState();\n }\n\n setConfig(config: Partial<SupportWidgetConfig>): void {\n this.config = {\n ...this.config,\n ...config,\n };\n if (config.projectId !== undefined) {\n this.setAttribute(\"project-id\", config.projectId);\n }\n if (config.isDev !== undefined) {\n if (config.isDev) {\n this.setAttribute(\"is-dev\", \"\");\n } else {\n this.removeAttribute(\"is-dev\");\n }\n }\n if (config.priority !== undefined) {\n this.priority = config.priority;\n }\n if (config.coordinators !== undefined) {\n this.coordinators = config.coordinators;\n }\n if (config.emailContacts !== undefined) {\n this.emailContacts = config.emailContacts;\n }\n if (config.toastDuration !== undefined) {\n const duration = config.toastDuration;\n if (typeof duration === \"number\") {\n const val = this.normalizeToastDuration(duration);\n this.toastSuccessDuration = val;\n this.toastErrorDuration = val;\n } else if (duration && typeof duration === \"object\") {\n if (duration.success !== undefined) {\n this.toastSuccessDuration = this.normalizeToastDuration(duration.success);\n } else {\n this.toastSuccessDuration = 4000;\n }\n if (duration.error !== undefined) {\n this.toastErrorDuration = this.normalizeToastDuration(duration.error);\n } else {\n this.toastErrorDuration = 4000;\n }\n }\n }\n }\n\n setFormPrefill(data: FormPrefillInput): void {\n this.setInputValue(\"content\", data.content ?? \"\");\n this.prefilledAttachments = data.attachments ?? [];\n this.renderFileList();\n }\n\n open(): void {\n this.shadowRoot?.getElementById(\"modal\")?.classList.add(\"show\");\n this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.OPEN, { bubbles: true }));\n }\n\n close(): void {\n this.clearCloseAfterSuccessTimer();\n this.resetFormData();\n this.hideToast(false);\n this.shadowRoot?.getElementById(\"modal\")?.classList.remove(\"show\");\n this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.CLOSE, { bubbles: true }));\n }\n\n public showDemoToast(type: \"success\" | \"error\"): void {\n if (type === \"success\") {\n this.showToast({\n type: \"success\",\n title: \"Gửi yêu cầu thành công\",\n message: \"Successful.\",\n requestCode: \"2607-230-1322\",\n url: \"https://hotro.azurewebsites.net/yeu-cau/2607-230-1322\",\n });\n } else {\n this.showToast({\n type: \"error\",\n title: \"Gửi yêu cầu thất bại\",\n message: \"Phone number is invalid\",\n });\n }\n }\n\n private syncConfigFromAttributes(): void {\n this.config = {\n ...this.config,\n projectId: this.getAttribute(\"project-id\") ?? \"\",\n isDev: this.hasAttribute(\"is-dev\"),\n };\n\n const toastDurationAttr = this.getAttribute(\"toast-duration\");\n if (toastDurationAttr !== null) {\n const trimmed = toastDurationAttr.trim();\n if (trimmed.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(trimmed);\n if (parsed && typeof parsed === \"object\") {\n if (parsed.success !== undefined) {\n this.toastSuccessDuration = this.normalizeToastDuration(Number(parsed.success));\n }\n if (parsed.error !== undefined) {\n this.toastErrorDuration = this.normalizeToastDuration(Number(parsed.error));\n }\n }\n } catch {\n // ignore parsing error\n }\n } else {\n const parsed = parseInt(trimmed, 10);\n if (!isNaN(parsed)) {\n const val = this.normalizeToastDuration(parsed);\n this.toastSuccessDuration = val;\n this.toastErrorDuration = val;\n }\n }\n }\n }\n\n private bindActions(): void {\n const root = this.shadowRoot;\n if (!root) return;\n\n const launcher = root.getElementById(\"open-btn\");\n if (launcher) {\n launcher.addEventListener(\"click\", () => {\n if (this.dragOccurred) {\n this.dragOccurred = false;\n return;\n }\n this.open();\n });\n this.setupDrag(launcher);\n }\n\n root\n .getElementById(\"close-btn\")\n ?.addEventListener(\"click\", () => this.close());\n root\n .getElementById(\"cancel-btn\")\n ?.addEventListener(\"click\", () => this.close());\n root\n .getElementById(\"submit-btn\")\n ?.addEventListener(\"click\", () => void this.submit());\n root.getElementById(\"toast-close-btn\")?.addEventListener(\"click\", () => {\n this.hideToast();\n });\n\n const toastCardEl = root.querySelector(\".toast-card\");\n if (toastCardEl) {\n toastCardEl.addEventListener(\"mouseenter\", () => {\n this.isToastPaused = true;\n });\n toastCardEl.addEventListener(\"mouseleave\", () => {\n this.isToastPaused = false;\n });\n }\n\n this.setupClearableInputs();\n\n // File selection UI handlers\n const uploadTrigger = root.getElementById(\"upload-trigger\");\n const fileInput = root.getElementById(\n \"file-input\",\n ) as HTMLInputElement | null;\n\n if (uploadTrigger && fileInput) {\n uploadTrigger.addEventListener(\"click\", () => {\n fileInput.click();\n });\n\n uploadTrigger.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n fileInput.click();\n }\n });\n\n uploadTrigger.addEventListener(\"dragover\", (e: DragEvent) => {\n e.preventDefault();\n if (e.dataTransfer) {\n e.dataTransfer.dropEffect = \"copy\";\n }\n uploadTrigger.classList.add(\"dragover\");\n });\n\n uploadTrigger.addEventListener(\"dragleave\", () => {\n uploadTrigger.classList.remove(\"dragover\");\n });\n\n uploadTrigger.addEventListener(\"drop\", (e: DragEvent) => {\n e.preventDefault();\n uploadTrigger.classList.remove(\"dragover\");\n if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\n this.addSelectedFiles(Array.from(e.dataTransfer.files));\n }\n });\n\n fileInput.addEventListener(\"change\", () => {\n if (fileInput.files) {\n this.addSelectedFiles(Array.from(fileInput.files));\n // Reset file input value to allow selecting the same file again\n fileInput.value = \"\";\n }\n });\n }\n\n this.setupToastCopy();\n this.setupClipboardPaste();\n this.setupPreviewControls();\n }\n\n private setupPreviewControls(): void {\n const root = this.shadowRoot;\n if (!root) return;\n\n const overlay = root.getElementById(\"preview-overlay\");\n root\n .getElementById(\"preview-close\")\n ?.addEventListener(\"click\", () => this.closePreview());\n\n overlay?.addEventListener(\"click\", (event) => {\n if (event.target === overlay) this.closePreview();\n });\n\n root.addEventListener(\"keydown\", (event) => {\n if ((event as KeyboardEvent).key === \"Escape\") this.closePreview();\n });\n }\n\n private setupToastCopy(): void {\n const root = this.shadowRoot;\n if (!root) return;\n\n const copyCodeBtn = root.getElementById(\"toast-copy-code-btn\");\n const copyLinkBtn = root.getElementById(\"toast-copy-link-btn\");\n\n const copyText = async (text: string, btn: HTMLElement | null) => {\n if (!text || !btn) return;\n\n const doSuccessFeedback = () => {\n btn.classList.add(\"copied\");\n const copyIcon = btn.querySelector(\".icon-copy\");\n const copiedIcon = btn.querySelector(\".icon-copied\");\n if (copyIcon && copiedIcon) {\n copyIcon.classList.add(\"is-hidden\");\n copiedIcon.classList.remove(\"is-hidden\");\n }\n\n window.setTimeout(() => {\n btn.classList.remove(\"copied\");\n if (copyIcon && copiedIcon) {\n copyIcon.classList.remove(\"is-hidden\");\n copiedIcon.classList.add(\"is-hidden\");\n }\n }, 1500);\n };\n\n if (navigator.clipboard && window.isSecureContext) {\n try {\n await navigator.clipboard.writeText(text);\n doSuccessFeedback();\n return;\n } catch (err) {\n console.warn(\"navigator.clipboard.writeText failed, trying fallback: \", err);\n }\n }\n\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.opacity = \"0\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n document.execCommand(\"copy\");\n doSuccessFeedback();\n } catch (err) {\n console.error(\"Fallback copy failed: \", err);\n } finally {\n document.body.removeChild(textarea);\n }\n };\n\n copyCodeBtn?.addEventListener(\"click\", () => {\n const codeEl = root.getElementById(\"toast-request-code\");\n const text = codeEl?.textContent || \"\";\n void copyText(text, copyCodeBtn);\n });\n\n copyLinkBtn?.addEventListener(\"click\", () => {\n const linkEl = root.getElementById(\"toast-link\") as HTMLAnchorElement | null;\n const text = linkEl?.href || \"\";\n void copyText(text, copyLinkBtn);\n });\n }\n\n private setupClipboardPaste(): void {\n const dialog = this.shadowRoot?.querySelector(\".dialog\");\n if (!dialog) return;\n\n dialog.addEventListener(\"paste\", (event) => {\n const clipboard = (event as ClipboardEvent).clipboardData;\n if (!clipboard) return;\n\n const files = Array.from(clipboard.items)\n .filter((item: DataTransferItem) => item.kind === \"file\")\n .map((item: DataTransferItem) => item.getAsFile())\n .filter((file): file is File => file !== null);\n\n if (files.length === 0) return;\n\n event.preventDefault();\n this.addSelectedFiles(files);\n });\n }\n\n private addSelectedFiles(files: File[]): void {\n if (files.length === 0) return;\n this.selectedFiles.push(...files);\n this.renderFileList();\n }\n\n private renderFileList(): void {\n const fileListEl = this.shadowRoot?.getElementById(\"file-list\");\n if (!fileListEl) return;\n fileListEl.innerHTML = \"\";\n\n // Release object URLs created in the previous render to avoid leaks\n this.previewUrls.forEach((url) => URL.revokeObjectURL(url));\n this.previewUrls = [];\n\n // Prefilled URLs\n this.prefilledAttachments.forEach((url, index) => {\n const name = url.substring(url.lastIndexOf(\"/\") + 1) || url;\n const isImage = this.isImageName(name);\n const previewUrl = isImage ? url : null;\n fileListEl.appendChild(\n this.buildFileItem({\n name,\n title: url,\n previewUrl,\n type: \"prefilled\",\n index,\n isImage,\n openUrl: url,\n badge: \"Đã gán\",\n }),\n );\n });\n\n // Selected local files\n this.selectedFiles.forEach((file, index) => {\n let previewUrl: string | null = null;\n const isImage = file.type.startsWith(\"image/\");\n if (isImage) {\n previewUrl = URL.createObjectURL(file);\n this.previewUrls.push(previewUrl);\n }\n fileListEl.appendChild(\n this.buildFileItem({\n name: file.name,\n title: file.name,\n previewUrl,\n type: \"selected\",\n index,\n isImage,\n openUrl: null,\n badge: null,\n }),\n );\n });\n\n // Hook remove event\n fileListEl.querySelectorAll(\".btn-remove\").forEach((btn) => {\n btn.addEventListener(\"click\", (e) => {\n const target = e.currentTarget as HTMLElement;\n const type = target.getAttribute(\"data-type\");\n const index = parseInt(target.getAttribute(\"data-index\") || \"0\", 10);\n if (type === \"prefilled\") {\n this.prefilledAttachments.splice(index, 1);\n } else {\n this.selectedFiles.splice(index, 1);\n }\n this.renderFileList();\n });\n });\n }\n\n private buildFileItem(opts: {\n name: string;\n title: string;\n previewUrl: string | null;\n type: \"prefilled\" | \"selected\";\n index: number;\n isImage: boolean;\n openUrl: string | null;\n badge: string | null;\n }): HTMLDivElement {\n const item = document.createElement(\"div\");\n item.className = \"file-item\";\n\n const trigger = document.createElement(\"button\");\n trigger.type = \"button\";\n trigger.className = \"file-preview-trigger\";\n trigger.title = \"Nhấn để xem trước\";\n trigger.addEventListener(\"click\", () => {\n this.openPreview({\n name: opts.title,\n previewUrl: opts.previewUrl,\n isImage: opts.isImage,\n openUrl: opts.openUrl,\n });\n });\n\n const thumb = document.createElement(\"div\");\n thumb.className = \"file-thumb\";\n if (opts.previewUrl) {\n const img = document.createElement(\"img\");\n img.src = opts.previewUrl;\n img.alt = opts.name;\n // Fall back to a file icon if the image fails to load\n img.addEventListener(\"error\", () => this.fillWithFileIcon(thumb));\n thumb.appendChild(img);\n } else {\n this.fillWithFileIcon(thumb);\n }\n\n const infoContainer = document.createElement(\"div\");\n infoContainer.className = \"file-info-container\";\n\n const name = document.createElement(\"span\");\n name.className = \"file-name\";\n name.title = opts.title;\n name.textContent = opts.name;\n\n infoContainer.appendChild(name);\n trigger.append(thumb, infoContainer);\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = \"btn-remove\";\n removeBtn.setAttribute(\"aria-label\", \"Xóa tệp\");\n removeBtn.setAttribute(\"data-type\", opts.type);\n removeBtn.setAttribute(\"data-index\", String(opts.index));\n removeBtn.innerHTML = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n `;\n\n item.append(trigger, removeBtn);\n\n if (opts.badge) {\n const badge = document.createElement(\"span\");\n badge.className = \"file-badge\";\n badge.textContent = opts.badge;\n item.appendChild(badge);\n }\n\n return item;\n }\n\n private fillWithFileIcon(thumb: HTMLElement): void {\n thumb.innerHTML = \"\";\n thumb.classList.add(\"file-thumb--icon\");\n thumb.innerHTML = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"></path>\n <polyline points=\"14 2 14 8 20 8\"></polyline>\n </svg>`;\n }\n\n private openPreview(opts: {\n name: string;\n previewUrl: string | null;\n isImage: boolean;\n openUrl: string | null;\n }): void {\n const overlay = this.shadowRoot?.getElementById(\"preview-overlay\");\n const body = this.shadowRoot?.getElementById(\"preview-body\");\n if (!overlay || !body) return;\n\n body.innerHTML = \"\";\n\n if (opts.isImage && opts.previewUrl) {\n const img = document.createElement(\"img\");\n img.src = opts.previewUrl;\n img.alt = opts.name;\n body.appendChild(img);\n } else {\n const info = document.createElement(\"div\");\n info.className = \"preview-file\";\n const openLink = opts.openUrl\n ? `<a href=\"${opts.openUrl}\" target=\"_blank\" rel=\"noopener noreferrer\">Mở trong tab mới</a>`\n : `<span>Không có bản xem trước cho tệp này</span>`;\n info.innerHTML = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"56\" height=\"56\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"></path>\n <polyline points=\"14 2 14 8 20 8\"></polyline>\n </svg>\n <span class=\"preview-file-name\"></span>\n ${openLink}`;\n const nameEl = info.querySelector(\".preview-file-name\");\n if (nameEl) nameEl.textContent = opts.name;\n body.appendChild(info);\n }\n\n overlay.classList.add(\"show\");\n }\n\n private closePreview(): void {\n const overlay = this.shadowRoot?.getElementById(\"preview-overlay\");\n if (!overlay) return;\n overlay.classList.remove(\"show\");\n const body = this.shadowRoot?.getElementById(\"preview-body\");\n if (body) body.innerHTML = \"\";\n }\n\n private isImageName(name: string): boolean {\n return /\\.(png|jpe?g|gif|webp|bmp|svg|avif)$/i.test(name);\n }\n\n private prefillFromState(): void {\n this.setInputValue(\"requester\", this.user?.name ?? \"\");\n this.setInputValue(\"email\", this.user?.email ?? \"\");\n this.setInputValue(\"phone-number\", this.user?.phoneNumber ?? \"\");\n }\n\n private applyLauncherStyle(): void {\n const launcher = this.shadowRoot?.getElementById(\"open-btn\");\n if (!launcher) return;\n\n const launcherStyles = [`--np-hub-icon: url(\"${npSupportLogo}\")`];\n const width = this.normalizeSize(this.getAttribute(\"width\"));\n const height = this.normalizeSize(this.getAttribute(\"height\"));\n if (width) launcherStyles.push(`--np-hub-width: ${width}`);\n if (height) launcherStyles.push(`--np-hub-height: ${height}`);\n\n launcher.setAttribute(\"style\", `${launcherStyles.join(\"; \")};`);\n }\n\n private applyPositionStyle(): void {\n // Skip when user already dragged the launcher (inline left/top set).\n if (this.style.left && this.style.top) return;\n\n const right = this.normalizeSize(this.getAttribute(\"right\"));\n const bottom = this.normalizeSize(this.getAttribute(\"bottom\"));\n const left = this.normalizeSize(this.getAttribute(\"left\"));\n const top = this.normalizeSize(this.getAttribute(\"top\"));\n\n this.setPositionVar(\"--np-hub-left\", left);\n this.setPositionVar(\"--np-hub-top\", top);\n this.setPositionVar(\"--np-hub-right\", right);\n this.setPositionVar(\"--np-hub-bottom\", bottom);\n\n // Avoid conflicting edges when only one side of a pair is given.\n if (left && !right) this.style.setProperty(\"--np-hub-right\", \"auto\");\n if (right && !left) this.style.setProperty(\"--np-hub-left\", \"auto\");\n if (top && !bottom) this.style.setProperty(\"--np-hub-bottom\", \"auto\");\n if (bottom && !top) this.style.setProperty(\"--np-hub-top\", \"auto\");\n }\n\n private setPositionVar(name: string, value: string | null): void {\n if (value) {\n this.style.setProperty(name, value);\n } else {\n this.style.removeProperty(name);\n }\n }\n\n private normalizeSize(value: string | null): string | null {\n const trimmed = value?.trim();\n if (!trimmed) return null;\n return /^\\d+(\\.\\d+)?$/.test(trimmed) ? `${trimmed}px` : trimmed;\n }\n\n private setupClearableInputs(): void {\n const root = this.shadowRoot;\n if (!root) return;\n\n root.querySelectorAll(\".input-wrap\").forEach((wrap) => {\n const input = wrap.querySelector(\"input, textarea\") as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n const clearBtn = wrap.querySelector(\n \".btn-clear\",\n ) as HTMLButtonElement | null;\n if (!input || !clearBtn) return;\n\n const updateVisibility = () => this.updateClearButton(input);\n\n input.addEventListener(\"input\", updateVisibility);\n input.addEventListener(\"input\", () => this.clearFieldError(input.id));\n clearBtn.addEventListener(\"click\", () => {\n input.value = \"\";\n input.focus();\n updateVisibility();\n this.clearFieldError(input.id);\n });\n\n updateVisibility();\n });\n }\n\n private updateClearButton(\n input: HTMLInputElement | HTMLTextAreaElement,\n ): void {\n const wrap = input.parentElement;\n if (wrap?.classList.contains(\"input-wrap\")) {\n wrap.classList.toggle(\"has-value\", input.value.length > 0);\n }\n }\n\n private setInputValue(id: string, value: string): void {\n const el = this.shadowRoot?.getElementById(id) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n if (!el) return;\n el.value = value;\n this.updateClearButton(el);\n }\n\n private getInputValue(id: string): string {\n const el = this.shadowRoot?.getElementById(id) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n return el?.value?.trim() ?? \"\";\n }\n\n private setFieldError(id: RequiredFieldId): void {\n const errorEl = this.shadowRoot?.getElementById(`${id}-error`);\n if (errorEl) {\n errorEl.textContent = \"\";\n }\n\n const input = this.shadowRoot?.getElementById(id) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n const wrap = input?.parentElement;\n if (wrap?.classList.contains(\"input-wrap\")) {\n wrap.classList.add(\"has-error\");\n }\n }\n\n private clearFieldError(id: string): void {\n const errorEl = this.shadowRoot?.getElementById(`${id}-error`);\n if (errorEl) {\n errorEl.textContent = \"\";\n }\n\n const input = this.shadowRoot?.getElementById(id) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n const wrap = input?.parentElement;\n if (wrap?.classList.contains(\"input-wrap\")) {\n wrap.classList.remove(\"has-error\");\n }\n }\n\n private clearAllFieldErrors(): void {\n this.clearFieldError(\"requester\");\n this.clearFieldError(\"phone-number\");\n this.clearFieldError(\"email\");\n this.clearFieldError(\"content\");\n }\n\n private showToast(\n payload: ToastPayload,\n autoCloseMs?: number,\n ): void {\n const toastEl = this.shadowRoot?.getElementById(\"toast\");\n const titleEl = this.shadowRoot?.getElementById(\"toast-title\");\n const messageEl = this.shadowRoot?.getElementById(\"toast-message\");\n const detailsEl = this.shadowRoot?.getElementById(\"toast-details\");\n const requestCodeEl = this.shadowRoot?.getElementById(\"toast-request-code\");\n const linkRowEl = this.shadowRoot?.getElementById(\"toast-link-row\");\n const linkEl = this.shadowRoot?.getElementById(\n \"toast-link\",\n ) as HTMLAnchorElement | null;\n const countdownEl = this.shadowRoot?.getElementById(\"toast-countdown\");\n const countdownSecondsEl = this.shadowRoot?.getElementById(\n \"toast-countdown-seconds\",\n );\n const countdownBarEl = this.shadowRoot?.getElementById(\n \"toast-countdown-bar\",\n );\n if (\n !toastEl ||\n !titleEl ||\n !messageEl ||\n !detailsEl ||\n !requestCodeEl ||\n !linkRowEl ||\n !linkEl ||\n !countdownBarEl\n ) {\n return;\n }\n\n this.clearToastAutoCloseTimer();\n this.clearToastCountdown();\n\n titleEl.textContent = payload.title;\n messageEl.textContent = payload.message;\n\n const hasRequestCode = Boolean(payload.requestCode);\n const hasUrl = Boolean(payload.url);\n const showDetails =\n payload.type === \"success\" && (hasRequestCode || hasUrl);\n\n if (showDetails) {\n requestCodeEl.textContent = payload.requestCode ?? \"—\";\n if (hasUrl && payload.url) {\n linkEl.href = payload.url;\n linkEl.textContent = payload.url;\n linkEl.title = payload.url;\n linkRowEl.classList.remove(\"is-hidden\");\n } else {\n linkEl.removeAttribute(\"href\");\n linkEl.textContent = \"\";\n linkEl.removeAttribute(\"title\");\n linkRowEl.classList.add(\"is-hidden\");\n }\n detailsEl.classList.remove(\"is-hidden\");\n } else {\n requestCodeEl.textContent = \"\";\n linkEl.removeAttribute(\"href\");\n linkEl.textContent = \"\";\n linkEl.removeAttribute(\"title\");\n linkRowEl.classList.add(\"is-hidden\");\n detailsEl.classList.add(\"is-hidden\");\n }\n\n const duration = autoCloseMs !== undefined\n ? autoCloseMs\n : (payload.type === \"success\" ? this.toastSuccessDuration : this.toastErrorDuration);\n\n this.toastRemainingMs = duration;\n this.toastTotalMs = duration;\n const toastCardEl = this.shadowRoot?.querySelector(\".toast-card\");\n this.isToastPaused = Boolean(toastCardEl?.matches(\":hover\"));\n\n toastEl.classList.remove(\"success\", \"error\");\n toastEl.classList.add(payload.type, \"show\");\n this.startToastCountdown(\n countdownEl ?? null,\n countdownSecondsEl ?? null,\n countdownBarEl,\n );\n }\n\n private hideToast(shouldCloseModal = true): void {\n const toastEl = this.shadowRoot?.getElementById(\"toast\");\n const titleEl = this.shadowRoot?.getElementById(\"toast-title\");\n const messageEl = this.shadowRoot?.getElementById(\"toast-message\");\n const detailsEl = this.shadowRoot?.getElementById(\"toast-details\");\n const requestCodeEl = this.shadowRoot?.getElementById(\"toast-request-code\");\n const linkRowEl = this.shadowRoot?.getElementById(\"toast-link-row\");\n const linkEl = this.shadowRoot?.getElementById(\n \"toast-link\",\n ) as HTMLAnchorElement | null;\n const countdownEl = this.shadowRoot?.getElementById(\"toast-countdown\");\n const countdownSecondsEl = this.shadowRoot?.getElementById(\n \"toast-countdown-seconds\",\n );\n const countdownBarEl = this.shadowRoot?.getElementById(\n \"toast-countdown-bar\",\n );\n if (\n !toastEl ||\n !titleEl ||\n !messageEl ||\n !detailsEl ||\n !requestCodeEl ||\n !linkRowEl ||\n !linkEl ||\n !countdownBarEl\n ) {\n return;\n }\n\n const isSuccess = toastEl.classList.contains(\"success\");\n\n this.clearToastAutoCloseTimer();\n this.clearToastCountdown();\n this.toastRemainingMs = 0;\n this.toastTotalMs = 0;\n this.isToastPaused = false;\n titleEl.textContent = \"\";\n messageEl.textContent = \"\";\n requestCodeEl.textContent = \"\";\n linkEl.removeAttribute(\"href\");\n linkEl.textContent = \"\";\n linkEl.removeAttribute(\"title\");\n linkRowEl.classList.add(\"is-hidden\");\n detailsEl.classList.add(\"is-hidden\");\n if (countdownSecondsEl) {\n countdownSecondsEl.textContent = \"\";\n }\n countdownBarEl.style.strokeDashoffset = \"0\";\n if (countdownEl) {\n countdownEl.classList.add(\"is-hidden\");\n }\n toastEl.classList.remove(\"success\", \"error\", \"show\");\n\n if (isSuccess && shouldCloseModal) {\n this.close();\n }\n }\n\n private startToastCountdown(\n countdownEl: HTMLElement | null,\n secondsEl: HTMLElement | null,\n barEl: HTMLElement,\n ): void {\n const updateCountdown = (): void => {\n const seconds = Math.max(1, Math.ceil(this.toastRemainingMs / 1000));\n if (secondsEl) {\n secondsEl.textContent = `${seconds}s`;\n }\n \n const percent = Math.max(0, Math.min(1, this.toastRemainingMs / this.toastTotalMs));\n const offset = 94.25 * (1 - percent);\n barEl.style.strokeDashoffset = `${offset}`;\n };\n\n updateCountdown();\n if (countdownEl) {\n countdownEl.classList.remove(\"is-hidden\");\n }\n\n this.toastCountdownInterval = window.setInterval(() => {\n if (this.isToastPaused) return;\n\n this.toastRemainingMs -= 100;\n if (this.toastRemainingMs <= 0) {\n this.toastRemainingMs = 0;\n barEl.style.strokeDashoffset = \"94.25\";\n if (secondsEl) {\n secondsEl.textContent = \"0s\";\n }\n this.hideToast();\n return;\n }\n updateCountdown();\n }, 100);\n }\n\n private clearToastCountdown(): void {\n if (this.toastCountdownInterval !== null) {\n window.clearInterval(this.toastCountdownInterval);\n this.toastCountdownInterval = null;\n }\n }\n\n private toToastPayload(content: SubmitToastContent): ToastPayload {\n return {\n ...content,\n title:\n content.type === \"success\"\n ? \"Gửi yêu cầu thành công\"\n : \"Gửi yêu cầu thất bại\",\n };\n }\n\n private clearToastAutoCloseTimer(): void {\n if (this.toastAutoCloseTimer !== null) {\n window.clearTimeout(this.toastAutoCloseTimer);\n this.toastAutoCloseTimer = null;\n }\n }\n\n private normalizeToastDuration(value: number): number {\n if (!Number.isFinite(value) || value <= 0) {\n return 4000;\n }\n return Math.floor(value);\n }\n\n private clearCloseAfterSuccessTimer(): void {\n if (this.closeAfterSuccessTimer !== null) {\n window.clearTimeout(this.closeAfterSuccessTimer);\n this.closeAfterSuccessTimer = null;\n }\n }\n\n private validateRequiredFields(): boolean {\n this.clearAllFieldErrors();\n\n const requiredFields: RequiredFieldId[] = [\n \"requester\",\n \"phone-number\",\n \"email\",\n \"content\",\n ];\n\n let firstInvalidId: RequiredFieldId | null = null;\n\n requiredFields.forEach((id) => {\n if (!this.getInputValue(id)) {\n this.setFieldError(id);\n if (!firstInvalidId) {\n firstInvalidId = id;\n }\n }\n });\n\n if (firstInvalidId) {\n const firstInvalid = this.shadowRoot?.getElementById(firstInvalidId) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n firstInvalid?.focus();\n return false;\n }\n\n return true;\n }\n\n private resetFormData(): void {\n this.setInputValue(\"requester\", \"\");\n this.setInputValue(\"email\", \"\");\n this.setInputValue(\"phone-number\", \"\");\n this.setInputValue(\"content\", \"\");\n this.clearAllFieldErrors();\n\n this.selectedFiles = [];\n this.prefilledAttachments = [];\n this.renderFileList();\n this.closePreview();\n }\n\n private async submit(): Promise<void> {\n this.clearCloseAfterSuccessTimer();\n this.hideToast(false);\n\n if (!this.validateRequiredFields()) {\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {\n bubbles: true,\n detail: { message: \"Please fill in all required fields.\" },\n }),\n );\n return;\n }\n\n const user: SupportUser = {\n name: this.getInputValue(\"requester\"),\n email: this.getInputValue(\"email\"),\n phoneNumber: this.getInputValue(\"phone-number\"),\n };\n\n this.user = user;\n\n try {\n validateWidgetConfig(this.config);\n validateUser(user);\n\n const content = this.getInputValue(\"content\");\n const submission: SupportSubmissionInput = {\n content,\n attachments: [], // dummy value for local validation of required content field\n };\n validateSubmission(submission);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Invalid input.\";\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {\n bubbles: true,\n detail: { message },\n }),\n );\n return;\n }\n\n const content = this.getInputValue(\"content\");\n\n // Create FormData to send request as multipart/form-data\n const formData = new FormData();\n formData.append(\"Requester\", user.name);\n formData.append(\"PhoneNumber\", user.phoneNumber);\n formData.append(\"Email\", user.email);\n formData.append(\"Content\", content);\n if (this.config.projectId?.trim()) {\n formData.append(\"ProjectId\", this.config.projectId.trim());\n }\n if (typeof this.priority === \"number\") {\n formData.append(\"Priority\", String(this.priority));\n }\n\n // Fetch prefilled attachments and convert them to Files\n for (const url of this.prefilledAttachments) {\n const file = await urlToFile(url);\n if (file) {\n formData.append(\"Attachments\", file);\n }\n }\n\n // Add newly selected files\n for (const file of this.selectedFiles) {\n formData.append(\"Attachments\", file);\n }\n\n // Add coordinators and emailContacts\n if (this.coordinators.length > 0) {\n this.coordinators.forEach((coord) =>\n formData.append(\"Coordinators\", coord),\n );\n }\n if (this.emailContacts.length > 0) {\n this.emailContacts.forEach((contact) =>\n formData.append(\"EmailContacts\", contact),\n );\n }\n\n try {\n const baseUrl = resolveBaseUrl(this.config);\n const result = await createSupportRequest(baseUrl, formData);\n\n if (!isCreateRequestSuccess(result)) {\n const failureContent = mapCreateRequestFailure(result);\n this.showToast(this.toToastPayload(failureContent));\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {\n bubbles: true,\n detail: { message: failureContent.message, response: result },\n }),\n );\n return;\n }\n\n const successContent = mapCreateRequestSuccess(result);\n this.showToast(this.toToastPayload(successContent));\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_SUCCESS, {\n bubbles: true,\n detail: result,\n }),\n );\n this.closeAfterSuccessTimer = window.setTimeout(() => {\n this.hideToast();\n }, this.toastSuccessDuration);\n } catch (error) {\n const errorContent = mapHttpClientError(error);\n this.showToast(this.toToastPayload(errorContent));\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {\n bubbles: true,\n detail: { message: errorContent.message },\n }),\n );\n }\n }\n\n private setupDrag(launcher: HTMLElement): void {\n launcher.addEventListener(\"pointerdown\", (e: PointerEvent) => {\n if (e.button !== 0) return;\n\n this.dragOccurred = false;\n const rect = this.getBoundingClientRect();\n const startX = e.clientX;\n const startY = e.clientY;\n const initialLeft = rect.left;\n const initialTop = rect.top;\n\n try {\n launcher.setPointerCapture(e.pointerId);\n } catch {\n // ignore if pointer capture fails\n }\n\n const onPointerMove = (moveEvent: PointerEvent) => {\n const dx = moveEvent.clientX - startX;\n const dy = moveEvent.clientY - startY;\n\n if (!this.dragOccurred && (Math.abs(dx) > 4 || Math.abs(dy) > 4)) {\n this.dragOccurred = true;\n launcher.style.cursor = \"grabbing\";\n }\n\n if (this.dragOccurred) {\n const leftVal = initialLeft + dx;\n const topVal = initialTop + dy;\n this.adjustPosition(leftVal, topVal);\n }\n };\n\n const onPointerUp = (upEvent: PointerEvent) => {\n try {\n launcher.releasePointerCapture(upEvent.pointerId);\n } catch {\n // ignore\n }\n launcher.removeEventListener(\"pointermove\", onPointerMove);\n launcher.removeEventListener(\"pointerup\", onPointerUp);\n launcher.removeEventListener(\"pointercancel\", onPointerUp);\n launcher.style.cursor = \"\";\n\n if (this.dragOccurred) {\n localStorage.setItem(\n \"np-hub-widget-position\",\n JSON.stringify({\n left: this.style.left,\n top: this.style.top,\n }),\n );\n }\n };\n\n launcher.addEventListener(\"pointermove\", onPointerMove);\n launcher.addEventListener(\"pointerup\", onPointerUp);\n launcher.addEventListener(\"pointercancel\", onPointerUp);\n });\n }\n\n private adjustPosition(leftVal: number, topVal: number): void {\n const rect = this.getBoundingClientRect();\n const width = rect.width || 72;\n const height = rect.height || 72;\n\n const minLeft = 0;\n const maxLeft = window.innerWidth - width;\n const minTop = 0;\n const maxTop = window.innerHeight - height;\n\n const clampedLeft = Math.max(minLeft, Math.min(maxLeft, leftVal));\n const clampedTop = Math.max(minTop, Math.min(maxTop, topVal));\n\n this.style.right = \"auto\";\n this.style.bottom = \"auto\";\n this.style.left = `${clampedLeft}px`;\n this.style.top = `${clampedTop}px`;\n }\n\n private restorePosition(): void {\n const savedPosition = localStorage.getItem(\"np-hub-widget-position\");\n if (savedPosition) {\n try {\n const { left, top } = JSON.parse(savedPosition);\n if (left && top) {\n const leftVal = parseFloat(left);\n const topVal = parseFloat(top);\n this.adjustPosition(leftVal, topVal);\n }\n } catch {\n // Ignore parsing errors\n }\n }\n }\n\n private handleResize = (): void => {\n if (this.style.left && this.style.top) {\n const leftVal = parseFloat(this.style.left);\n const topVal = parseFloat(this.style.top);\n this.adjustPosition(leftVal, topVal);\n }\n };\n}\n","import { SupportWidgetElement } from \"./component/support-widget.element\";\n\nconst TAG_NAME = \"np-hub\";\n\nexport function registerSupportWidget(): void {\n if (!customElements.get(TAG_NAME)) {\n customElements.define(TAG_NAME, SupportWidgetElement);\n }\n}\n\nregisterSupportWidget();\n"],"mappings":";AAAO,IAAM,eAAe;AACrB,IAAM,cAAc;AAEpB,SAAS,eAAe,OAAoC;AACjE,SAAO,MAAM,QAAQ,cAAc;AACrC;;;ACCO,SAAS,qBAAqB,QAAmC;AACtE,MAAI,OAAO,cAAc,UAAa,OAAO,UAAU,KAAK,EAAE,WAAW,GAAG;AAC1E,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AACF;AAEO,SAAS,aAAa,MAAyB;AAZtD;AAaE,MAAI,GAAC,UAAK,SAAL,mBAAW,SAAQ;AACtB,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,MAAI,GAAC,UAAK,UAAL,mBAAY,SAAQ;AACvB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACA,MAAI,GAAC,UAAK,gBAAL,mBAAkB,SAAQ;AAC7B,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AACF;AAEO,SAAS,mBAAmB,OAAqC;AAxBxE;AAyBE,MAAI,GAAC,WAAM,YAAN,mBAAe,SAAQ;AAC1B,UAAM,IAAI,MAAM,sBAAsB;AAAA,EACxC;AACF;;;ACVA,IAAM,2BAA2B;AACjC,IAAM,yBAAyB;AAExB,SAAS,uBACd,QACS;AACT,MAAI,OAAO,OAAO,eAAe,UAAU;AACzC,WAAO,OAAO,cAAc,OAAO,OAAO,aAAa;AAAA,EACzD;AACA,SAAO;AACT;AAEO,SAAS,wBACd,QACoB;AAhCtB;AAiCE,QAAM,aACJ,OAAO,OAAO,eAAe,WACzB,OAAO,OAAO,UAAU,IACxB;AACN,QAAM,eAAc,wBAAO,SAAP,mBAAa,gBAAb,mBAA0B,WAA1B,YAAoC;AACxD,QAAM,OAAM,wBAAO,SAAP,mBAAa,QAAb,mBAAkB,WAAlB,YAA4B;AAExC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAS,YAAO,YAAP,mBAAgB,WAAU;AAAA,IACnC;AAAA,IACA,aAAa,eAAe;AAAA,IAC5B,KAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,wBACd,QACoB;AAnDtB;AAoDE,QAAM,aACJ,OAAO,OAAO,eAAe,WACzB,OAAO,OAAO,UAAU,IACxB;AAEN,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAS,YAAO,YAAP,mBAAgB,WAAU;AAAA,IACnC;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,OAAoC;AAhEvE;AAiEE,QAAM,kBAAkB;AACxB,QAAM,aACJ,iBAAiB,QAAQ,MAAM,UAAU;AAC3C,QAAM,gBAAgB,WAAW,MAAM,2BAA2B;AAClE,QAAM,aAAa,gBAAgB,cAAc,CAAC,IAAI;AACtD,QAAM,aAAa,WAAW,MAAM,kCAAkC;AACtE,QAAM,eAAc,oDAAa,OAAb,mBAAiB,WAAjB,YAA2B;AAE/C,MAAI,gBAAgB;AACpB,MAAI,aAAa;AACjB,MAAI,YAAY,WAAW,GAAG,GAAG;AAC/B,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,WAAW;AACrC,UAAI,OAAO,OAAO,eAAe,UAAU;AACzC,qBAAa,OAAO,OAAO,UAAU;AAAA,MACvC;AACA,UAAI,OAAO,OAAO,YAAY,UAAU;AACtC,wBAAgB,OAAO,QAAQ,KAAK;AAAA,MACtC;AAAA,IACF,SAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,iBAAiB,aAAa;AACjC,oBAAgB;AAAA,EAClB;AACA,MAAI,CAAC,eAAe;AAClB,oBAAgB,cAAc;AAAA,EAChC;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,YAAY,cAAc,cAAc;AAAA,EAC1C;AACF;;;ACrGA,eAAsB,aACpB,KACA,MACoB;AACpB,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IACR;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,UAAM,IAAI,MAAM,mBAAmB,SAAS,MAAM,MAAM,OAAO,EAAE;AAAA,EACnE;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;;;ACVA,eAAsB,qBACpB,SACA,UACgC;AAChC,QAAM,WAAW,GAAG,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAC9C,SAAO,aAAoC,UAAU,QAAQ;AAC/D;;;ACXO,IAAM,gBAAgB;AAAA,EAC3B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,cAAc;AAChB;A;;;;;ACLA;;;ACAA;;;ACyBA,IAAI,iBAA6C;AAEjD,SAAS,cAAmC;AAC1C,MAAI,CAAC,gBAAgB;AACnB,qBAAiB,SAAS,cAAc,UAAU;AAClD,mBAAe,YAAY,UAAU,6BAAM,WAAW,+BAAM;AAAA,EAC9D;AACA,SAAO;AACT;AAEA,eAAe,UAAU,KAAmC;AAC1D,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG;AAChC,QAAI,CAAC,SAAS,GAAI,QAAO;AACzB,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,OAAO,IAAI,UAAU,IAAI,YAAY,GAAG,IAAI,CAAC,KAAK;AACxD,WAAO,IAAI,KAAK,CAAC,IAAI,GAAG,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,EACnD,SAAS,GAAG;AACV,YAAQ,KAAK,wCAAwC,KAAK,CAAC;AAC3D,WAAO;AAAA,EACT;AACF;AAaO,IAAM,uBAAN,cAAmC,YAAY;AAAA,EAoCpD,cAAc;AACZ,UAAM;AAxBR,SAAQ,SAA8B;AAAA,MACpC,WAAW;AAAA,IACb;AAGA,SAAQ,eAAe;AAGvB;AAAA,SAAQ,gBAAwB,CAAC;AACjC,SAAQ,uBAAiC,CAAC;AAC1C,SAAQ,cAAwB,CAAC;AACjC,SAAQ,yBAAwC;AAChD,SAAQ,sBAAqC;AAC7C,SAAQ,yBAAwC;AAChD,SAAQ,uBAAuB;AAC/B,SAAQ,qBAAqB;AAC7B,SAAQ,mBAAmB;AAC3B,SAAQ,eAAe;AACvB,SAAQ,gBAAgB;AAExB,SAAO,eAAyB,CAAC;AACjC,SAAO,gBAA0B,CAAC;AAwqClC,SAAQ,eAAe,MAAY;AACjC,UAAI,KAAK,MAAM,QAAQ,KAAK,MAAM,KAAK;AACrC,cAAM,UAAU,WAAW,KAAK,MAAM,IAAI;AAC1C,cAAM,SAAS,WAAW,KAAK,MAAM,GAAG;AACxC,aAAK,eAAe,SAAS,MAAM;AAAA,MACrC;AAAA,IACF;AA1qCE,UAAM,OAAO,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAC/C,SAAK,YAAY,YAAY,EAAE,QAAQ,UAAU,IAAI,CAAC;AACtD,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,oBAA0B;AACxB,SAAK,yBAAyB;AAC9B,SAAK,iBAAiB;AACtB,SAAK,mBAAmB;AACxB,SAAK,mBAAmB;AACxB,SAAK,gBAAgB;AACrB,WAAO,iBAAiB,UAAU,KAAK,YAAY;AAAA,EACrD;AAAA,EAEA,uBAA6B;AAC3B,WAAO,oBAAoB,UAAU,KAAK,YAAY;AACtD,SAAK,4BAA4B;AACjC,SAAK,yBAAyB;AAC9B,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEA,2BAAiC;AAC/B,SAAK,yBAAyB;AAC9B,SAAK,iBAAiB;AACtB,SAAK,mBAAmB;AACxB,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEA,QAAQ,MAAgC;AACtC,SAAK,OAAO;AACZ,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEA,UAAU,QAA4C;AACpD,SAAK,SAAS;AAAA,MACZ,GAAG,KAAK;AAAA,MACR,GAAG;AAAA,IACL;AACA,QAAI,OAAO,cAAc,QAAW;AAClC,WAAK,aAAa,cAAc,OAAO,SAAS;AAAA,IAClD;AACA,QAAI,OAAO,UAAU,QAAW;AAC9B,UAAI,OAAO,OAAO;AAChB,aAAK,aAAa,UAAU,EAAE;AAAA,MAChC,OAAO;AACL,aAAK,gBAAgB,QAAQ;AAAA,MAC/B;AAAA,IACF;AACA,QAAI,OAAO,aAAa,QAAW;AACjC,WAAK,WAAW,OAAO;AAAA,IACzB;AACA,QAAI,OAAO,iBAAiB,QAAW;AACrC,WAAK,eAAe,OAAO;AAAA,IAC7B;AACA,QAAI,OAAO,kBAAkB,QAAW;AACtC,WAAK,gBAAgB,OAAO;AAAA,IAC9B;AACA,QAAI,OAAO,kBAAkB,QAAW;AACtC,YAAM,WAAW,OAAO;AACxB,UAAI,OAAO,aAAa,UAAU;AAChC,cAAM,MAAM,KAAK,uBAAuB,QAAQ;AAChD,aAAK,uBAAuB;AAC5B,aAAK,qBAAqB;AAAA,MAC5B,WAAW,YAAY,OAAO,aAAa,UAAU;AACnD,YAAI,SAAS,YAAY,QAAW;AAClC,eAAK,uBAAuB,KAAK,uBAAuB,SAAS,OAAO;AAAA,QAC1E,OAAO;AACL,eAAK,uBAAuB;AAAA,QAC9B;AACA,YAAI,SAAS,UAAU,QAAW;AAChC,eAAK,qBAAqB,KAAK,uBAAuB,SAAS,KAAK;AAAA,QACtE,OAAO;AACL,eAAK,qBAAqB;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eAAe,MAA8B;AA/K/C;AAgLI,SAAK,cAAc,YAAW,UAAK,YAAL,YAAgB,EAAE;AAChD,SAAK,wBAAuB,UAAK,gBAAL,YAAoB,CAAC;AACjD,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,OAAa;AArLf;AAsLI,qBAAK,eAAL,mBAAiB,eAAe,aAAhC,mBAA0C,UAAU,IAAI;AACxD,SAAK,cAAc,IAAI,YAAY,cAAc,MAAM,EAAE,SAAS,KAAK,CAAC,CAAC;AAAA,EAC3E;AAAA,EAEA,QAAc;AA1LhB;AA2LI,SAAK,4BAA4B;AACjC,SAAK,cAAc;AACnB,SAAK,UAAU,KAAK;AACpB,qBAAK,eAAL,mBAAiB,eAAe,aAAhC,mBAA0C,UAAU,OAAO;AAC3D,SAAK,cAAc,IAAI,YAAY,cAAc,OAAO,EAAE,SAAS,KAAK,CAAC,CAAC;AAAA,EAC5E;AAAA,EAEO,cAAc,MAAiC;AACpD,QAAI,SAAS,WAAW;AACtB,WAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,OAAO;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,QACb,KAAK;AAAA,MACP,CAAC;AAAA,IACH,OAAO;AACL,WAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,OAAO;AAAA,QACP,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,2BAAiC;AApN3C;AAqNI,SAAK,SAAS;AAAA,MACZ,GAAG,KAAK;AAAA,MACR,YAAW,UAAK,aAAa,YAAY,MAA9B,YAAmC;AAAA,MAC9C,OAAO,KAAK,aAAa,QAAQ;AAAA,IACnC;AAEA,UAAM,oBAAoB,KAAK,aAAa,gBAAgB;AAC5D,QAAI,sBAAsB,MAAM;AAC9B,YAAM,UAAU,kBAAkB,KAAK;AACvC,UAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,OAAO;AACjC,cAAI,UAAU,OAAO,WAAW,UAAU;AACxC,gBAAI,OAAO,YAAY,QAAW;AAChC,mBAAK,uBAAuB,KAAK,uBAAuB,OAAO,OAAO,OAAO,CAAC;AAAA,YAChF;AACA,gBAAI,OAAO,UAAU,QAAW;AAC9B,mBAAK,qBAAqB,KAAK,uBAAuB,OAAO,OAAO,KAAK,CAAC;AAAA,YAC5E;AAAA,UACF;AAAA,QACF,SAAQ;AAAA,QAER;AAAA,MACF,OAAO;AACL,cAAM,SAAS,SAAS,SAAS,EAAE;AACnC,YAAI,CAAC,MAAM,MAAM,GAAG;AAClB,gBAAM,MAAM,KAAK,uBAAuB,MAAM;AAC9C,eAAK,uBAAuB;AAC5B,eAAK,qBAAqB;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,cAAoB;AAvP9B;AAwPI,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM;AAEX,UAAM,WAAW,KAAK,eAAe,UAAU;AAC/C,QAAI,UAAU;AACZ,eAAS,iBAAiB,SAAS,MAAM;AACvC,YAAI,KAAK,cAAc;AACrB,eAAK,eAAe;AACpB;AAAA,QACF;AACA,aAAK,KAAK;AAAA,MACZ,CAAC;AACD,WAAK,UAAU,QAAQ;AAAA,IACzB;AAEA,eACG,eAAe,WAAW,MAD7B,mBAEI,iBAAiB,SAAS,MAAM,KAAK,MAAM;AAC/C,eACG,eAAe,YAAY,MAD9B,mBAEI,iBAAiB,SAAS,MAAM,KAAK,MAAM;AAC/C,eACG,eAAe,YAAY,MAD9B,mBAEI,iBAAiB,SAAS,MAAM,KAAK,KAAK,OAAO;AACrD,eAAK,eAAe,iBAAiB,MAArC,mBAAwC,iBAAiB,SAAS,MAAM;AACtE,WAAK,UAAU;AAAA,IACjB;AAEA,UAAM,cAAc,KAAK,cAAc,aAAa;AACpD,QAAI,aAAa;AACf,kBAAY,iBAAiB,cAAc,MAAM;AAC/C,aAAK,gBAAgB;AAAA,MACvB,CAAC;AACD,kBAAY,iBAAiB,cAAc,MAAM;AAC/C,aAAK,gBAAgB;AAAA,MACvB,CAAC;AAAA,IACH;AAEA,SAAK,qBAAqB;AAG1B,UAAM,gBAAgB,KAAK,eAAe,gBAAgB;AAC1D,UAAM,YAAY,KAAK;AAAA,MACrB;AAAA,IACF;AAEA,QAAI,iBAAiB,WAAW;AAC9B,oBAAc,iBAAiB,SAAS,MAAM;AAC5C,kBAAU,MAAM;AAAA,MAClB,CAAC;AAED,oBAAc,iBAAiB,WAAW,CAAC,MAAqB;AAC9D,YAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,YAAE,eAAe;AACjB,oBAAU,MAAM;AAAA,QAClB;AAAA,MACF,CAAC;AAED,oBAAc,iBAAiB,YAAY,CAAC,MAAiB;AAC3D,UAAE,eAAe;AACjB,YAAI,EAAE,cAAc;AAClB,YAAE,aAAa,aAAa;AAAA,QAC9B;AACA,sBAAc,UAAU,IAAI,UAAU;AAAA,MACxC,CAAC;AAED,oBAAc,iBAAiB,aAAa,MAAM;AAChD,sBAAc,UAAU,OAAO,UAAU;AAAA,MAC3C,CAAC;AAED,oBAAc,iBAAiB,QAAQ,CAAC,MAAiB;AA9T/D,YAAAA;AA+TQ,UAAE,eAAe;AACjB,sBAAc,UAAU,OAAO,UAAU;AACzC,cAAIA,MAAA,EAAE,iBAAF,gBAAAA,IAAgB,UAAS,EAAE,aAAa,MAAM,SAAS,GAAG;AAC5D,eAAK,iBAAiB,MAAM,KAAK,EAAE,aAAa,KAAK,CAAC;AAAA,QACxD;AAAA,MACF,CAAC;AAED,gBAAU,iBAAiB,UAAU,MAAM;AACzC,YAAI,UAAU,OAAO;AACnB,eAAK,iBAAiB,MAAM,KAAK,UAAU,KAAK,CAAC;AAEjD,oBAAU,QAAQ;AAAA,QACpB;AAAA,MACF,CAAC;AAAA,IACH;AAEA,SAAK,eAAe;AACpB,SAAK,oBAAoB;AACzB,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEQ,uBAA6B;AApVvC;AAqVI,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM;AAEX,UAAM,UAAU,KAAK,eAAe,iBAAiB;AACrD,eACG,eAAe,eAAe,MADjC,mBAEI,iBAAiB,SAAS,MAAM,KAAK,aAAa;AAEtD,uCAAS,iBAAiB,SAAS,CAAC,UAAU;AAC5C,UAAI,MAAM,WAAW,QAAS,MAAK,aAAa;AAAA,IAClD;AAEA,SAAK,iBAAiB,WAAW,CAAC,UAAU;AAC1C,UAAK,MAAwB,QAAQ,SAAU,MAAK,aAAa;AAAA,IACnE,CAAC;AAAA,EACH;AAAA,EAEQ,iBAAuB;AAC7B,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM;AAEX,UAAM,cAAc,KAAK,eAAe,qBAAqB;AAC7D,UAAM,cAAc,KAAK,eAAe,qBAAqB;AAE7D,UAAM,WAAW,OAAO,MAAc,QAA4B;AAChE,UAAI,CAAC,QAAQ,CAAC,IAAK;AAEnB,YAAM,oBAAoB,MAAM;AAC9B,YAAI,UAAU,IAAI,QAAQ;AAC1B,cAAM,WAAW,IAAI,cAAc,YAAY;AAC/C,cAAM,aAAa,IAAI,cAAc,cAAc;AACnD,YAAI,YAAY,YAAY;AAC1B,mBAAS,UAAU,IAAI,WAAW;AAClC,qBAAW,UAAU,OAAO,WAAW;AAAA,QACzC;AAEA,eAAO,WAAW,MAAM;AACtB,cAAI,UAAU,OAAO,QAAQ;AAC7B,cAAI,YAAY,YAAY;AAC1B,qBAAS,UAAU,OAAO,WAAW;AACrC,uBAAW,UAAU,IAAI,WAAW;AAAA,UACtC;AAAA,QACF,GAAG,IAAI;AAAA,MACT;AAEA,UAAI,UAAU,aAAa,OAAO,iBAAiB;AACjD,YAAI;AACF,gBAAM,UAAU,UAAU,UAAU,IAAI;AACxC,4BAAkB;AAClB;AAAA,QACF,SAAS,KAAK;AACZ,kBAAQ,KAAK,2DAA2D,GAAG;AAAA,QAC7E;AAAA,MACF;AAEA,YAAM,WAAW,SAAS,cAAc,UAAU;AAClD,eAAS,QAAQ;AACjB,eAAS,MAAM,WAAW;AAC1B,eAAS,MAAM,UAAU;AACzB,eAAS,KAAK,YAAY,QAAQ;AAClC,eAAS,OAAO;AAChB,UAAI;AACF,iBAAS,YAAY,MAAM;AAC3B,0BAAkB;AAAA,MACpB,SAAS,KAAK;AACZ,gBAAQ,MAAM,0BAA0B,GAAG;AAAA,MAC7C,UAAE;AACA,iBAAS,KAAK,YAAY,QAAQ;AAAA,MACpC;AAAA,IACF;AAEA,+CAAa,iBAAiB,SAAS,MAAM;AAC3C,YAAM,SAAS,KAAK,eAAe,oBAAoB;AACvD,YAAM,QAAO,iCAAQ,gBAAe;AACpC,WAAK,SAAS,MAAM,WAAW;AAAA,IACjC;AAEA,+CAAa,iBAAiB,SAAS,MAAM;AAC3C,YAAM,SAAS,KAAK,eAAe,YAAY;AAC/C,YAAM,QAAO,iCAAQ,SAAQ;AAC7B,WAAK,SAAS,MAAM,WAAW;AAAA,IACjC;AAAA,EACF;AAAA,EAEQ,sBAA4B;AAzatC;AA0aI,UAAM,UAAS,UAAK,eAAL,mBAAiB,cAAc;AAC9C,QAAI,CAAC,OAAQ;AAEb,WAAO,iBAAiB,SAAS,CAAC,UAAU;AAC1C,YAAM,YAAa,MAAyB;AAC5C,UAAI,CAAC,UAAW;AAEhB,YAAM,QAAQ,MAAM,KAAK,UAAU,KAAK,EACrC,OAAO,CAAC,SAA2B,KAAK,SAAS,MAAM,EACvD,IAAI,CAAC,SAA2B,KAAK,UAAU,CAAC,EAChD,OAAO,CAAC,SAAuB,SAAS,IAAI;AAE/C,UAAI,MAAM,WAAW,EAAG;AAExB,YAAM,eAAe;AACrB,WAAK,iBAAiB,KAAK;AAAA,IAC7B,CAAC;AAAA,EACH;AAAA,EAEQ,iBAAiB,OAAqB;AAC5C,QAAI,MAAM,WAAW,EAAG;AACxB,SAAK,cAAc,KAAK,GAAG,KAAK;AAChC,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,iBAAuB;AAncjC;AAocI,UAAM,cAAa,UAAK,eAAL,mBAAiB,eAAe;AACnD,QAAI,CAAC,WAAY;AACjB,eAAW,YAAY;AAGvB,SAAK,YAAY,QAAQ,CAAC,QAAQ,IAAI,gBAAgB,GAAG,CAAC;AAC1D,SAAK,cAAc,CAAC;AAGpB,SAAK,qBAAqB,QAAQ,CAAC,KAAK,UAAU;AAChD,YAAM,OAAO,IAAI,UAAU,IAAI,YAAY,GAAG,IAAI,CAAC,KAAK;AACxD,YAAM,UAAU,KAAK,YAAY,IAAI;AACrC,YAAM,aAAa,UAAU,MAAM;AACnC,iBAAW;AAAA,QACT,KAAK,cAAc;AAAA,UACjB;AAAA,UACA,OAAO;AAAA,UACP;AAAA,UACA,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA,SAAS;AAAA,UACT,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,SAAK,cAAc,QAAQ,CAAC,MAAM,UAAU;AAC1C,UAAI,aAA4B;AAChC,YAAM,UAAU,KAAK,KAAK,WAAW,QAAQ;AAC7C,UAAI,SAAS;AACX,qBAAa,IAAI,gBAAgB,IAAI;AACrC,aAAK,YAAY,KAAK,UAAU;AAAA,MAClC;AACA,iBAAW;AAAA,QACT,KAAK,cAAc;AAAA,UACjB,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ;AAAA,UACA,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA,SAAS;AAAA,UACT,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,eAAW,iBAAiB,aAAa,EAAE,QAAQ,CAAC,QAAQ;AAC1D,UAAI,iBAAiB,SAAS,CAAC,MAAM;AACnC,cAAM,SAAS,EAAE;AACjB,cAAM,OAAO,OAAO,aAAa,WAAW;AAC5C,cAAM,QAAQ,SAAS,OAAO,aAAa,YAAY,KAAK,KAAK,EAAE;AACnE,YAAI,SAAS,aAAa;AACxB,eAAK,qBAAqB,OAAO,OAAO,CAAC;AAAA,QAC3C,OAAO;AACL,eAAK,cAAc,OAAO,OAAO,CAAC;AAAA,QACpC;AACA,aAAK,eAAe;AAAA,MACtB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEQ,cAAc,MASH;AACjB,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAK,YAAY;AAEjB,UAAM,UAAU,SAAS,cAAc,QAAQ;AAC/C,YAAQ,OAAO;AACf,YAAQ,YAAY;AACpB,YAAQ,QAAQ;AAChB,YAAQ,iBAAiB,SAAS,MAAM;AACtC,WAAK,YAAY;AAAA,QACf,MAAM,KAAK;AAAA,QACX,YAAY,KAAK;AAAA,QACjB,SAAS,KAAK;AAAA,QACd,SAAS,KAAK;AAAA,MAChB,CAAC;AAAA,IACH,CAAC;AAED,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY;AAClB,QAAI,KAAK,YAAY;AACnB,YAAM,MAAM,SAAS,cAAc,KAAK;AACxC,UAAI,MAAM,KAAK;AACf,UAAI,MAAM,KAAK;AAEf,UAAI,iBAAiB,SAAS,MAAM,KAAK,iBAAiB,KAAK,CAAC;AAChE,YAAM,YAAY,GAAG;AAAA,IACvB,OAAO;AACL,WAAK,iBAAiB,KAAK;AAAA,IAC7B;AAEA,UAAM,gBAAgB,SAAS,cAAc,KAAK;AAClD,kBAAc,YAAY;AAE1B,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,YAAY;AACjB,SAAK,QAAQ,KAAK;AAClB,SAAK,cAAc,KAAK;AAExB,kBAAc,YAAY,IAAI;AAC9B,YAAQ,OAAO,OAAO,aAAa;AAEnC,UAAM,YAAY,SAAS,cAAc,QAAQ;AACjD,cAAU,OAAO;AACjB,cAAU,YAAY;AACtB,cAAU,aAAa,cAAc,iBAAS;AAC9C,cAAU,aAAa,aAAa,KAAK,IAAI;AAC7C,cAAU,aAAa,cAAc,OAAO,KAAK,KAAK,CAAC;AACvD,cAAU,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAOtB,SAAK,OAAO,SAAS,SAAS;AAE9B,QAAI,KAAK,OAAO;AACd,YAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,YAAM,YAAY;AAClB,YAAM,cAAc,KAAK;AACzB,WAAK,YAAY,KAAK;AAAA,IACxB;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,OAA0B;AACjD,UAAM,YAAY;AAClB,UAAM,UAAU,IAAI,kBAAkB;AACtC,UAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpB;AAAA,EAEQ,YAAY,MAKX;AA/lBX;AAgmBI,UAAM,WAAU,UAAK,eAAL,mBAAiB,eAAe;AAChD,UAAM,QAAO,UAAK,eAAL,mBAAiB,eAAe;AAC7C,QAAI,CAAC,WAAW,CAAC,KAAM;AAEvB,SAAK,YAAY;AAEjB,QAAI,KAAK,WAAW,KAAK,YAAY;AACnC,YAAM,MAAM,SAAS,cAAc,KAAK;AACxC,UAAI,MAAM,KAAK;AACf,UAAI,MAAM,KAAK;AACf,WAAK,YAAY,GAAG;AAAA,IACtB,OAAO;AACL,YAAM,OAAO,SAAS,cAAc,KAAK;AACzC,WAAK,YAAY;AACjB,YAAM,WAAW,KAAK,UAClB,YAAY,KAAK,OAAO,+EACxB;AACJ,WAAK,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMb,QAAQ;AACZ,YAAM,SAAS,KAAK,cAAc,oBAAoB;AACtD,UAAI,OAAQ,QAAO,cAAc,KAAK;AACtC,WAAK,YAAY,IAAI;AAAA,IACvB;AAEA,YAAQ,UAAU,IAAI,MAAM;AAAA,EAC9B;AAAA,EAEQ,eAAqB;AAhoB/B;AAioBI,UAAM,WAAU,UAAK,eAAL,mBAAiB,eAAe;AAChD,QAAI,CAAC,QAAS;AACd,YAAQ,UAAU,OAAO,MAAM;AAC/B,UAAM,QAAO,UAAK,eAAL,mBAAiB,eAAe;AAC7C,QAAI,KAAM,MAAK,YAAY;AAAA,EAC7B;AAAA,EAEQ,YAAY,MAAuB;AACzC,WAAO,wCAAwC,KAAK,IAAI;AAAA,EAC1D;AAAA,EAEQ,mBAAyB;AA5oBnC;AA6oBI,SAAK,cAAc,cAAa,gBAAK,SAAL,mBAAW,SAAX,YAAmB,EAAE;AACrD,SAAK,cAAc,UAAS,gBAAK,SAAL,mBAAW,UAAX,YAAoB,EAAE;AAClD,SAAK,cAAc,iBAAgB,gBAAK,SAAL,mBAAW,gBAAX,YAA0B,EAAE;AAAA,EACjE;AAAA,EAEQ,qBAA2B;AAlpBrC;AAmpBI,UAAM,YAAW,UAAK,eAAL,mBAAiB,eAAe;AACjD,QAAI,CAAC,SAAU;AAEf,UAAM,iBAAiB,CAAC,uBAAuB,uBAAa,IAAI;AAChE,UAAM,QAAQ,KAAK,cAAc,KAAK,aAAa,OAAO,CAAC;AAC3D,UAAM,SAAS,KAAK,cAAc,KAAK,aAAa,QAAQ,CAAC;AAC7D,QAAI,MAAO,gBAAe,KAAK,mBAAmB,KAAK,EAAE;AACzD,QAAI,OAAQ,gBAAe,KAAK,oBAAoB,MAAM,EAAE;AAE5D,aAAS,aAAa,SAAS,GAAG,eAAe,KAAK,IAAI,CAAC,GAAG;AAAA,EAChE;AAAA,EAEQ,qBAA2B;AAEjC,QAAI,KAAK,MAAM,QAAQ,KAAK,MAAM,IAAK;AAEvC,UAAM,QAAQ,KAAK,cAAc,KAAK,aAAa,OAAO,CAAC;AAC3D,UAAM,SAAS,KAAK,cAAc,KAAK,aAAa,QAAQ,CAAC;AAC7D,UAAM,OAAO,KAAK,cAAc,KAAK,aAAa,MAAM,CAAC;AACzD,UAAM,MAAM,KAAK,cAAc,KAAK,aAAa,KAAK,CAAC;AAEvD,SAAK,eAAe,iBAAiB,IAAI;AACzC,SAAK,eAAe,gBAAgB,GAAG;AACvC,SAAK,eAAe,kBAAkB,KAAK;AAC3C,SAAK,eAAe,mBAAmB,MAAM;AAG7C,QAAI,QAAQ,CAAC,MAAO,MAAK,MAAM,YAAY,kBAAkB,MAAM;AACnE,QAAI,SAAS,CAAC,KAAM,MAAK,MAAM,YAAY,iBAAiB,MAAM;AAClE,QAAI,OAAO,CAAC,OAAQ,MAAK,MAAM,YAAY,mBAAmB,MAAM;AACpE,QAAI,UAAU,CAAC,IAAK,MAAK,MAAM,YAAY,gBAAgB,MAAM;AAAA,EACnE;AAAA,EAEQ,eAAe,MAAc,OAA4B;AAC/D,QAAI,OAAO;AACT,WAAK,MAAM,YAAY,MAAM,KAAK;AAAA,IACpC,OAAO;AACL,WAAK,MAAM,eAAe,IAAI;AAAA,IAChC;AAAA,EACF;AAAA,EAEQ,cAAc,OAAqC;AACzD,UAAM,UAAU,+BAAO;AACvB,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO,gBAAgB,KAAK,OAAO,IAAI,GAAG,OAAO,OAAO;AAAA,EAC1D;AAAA,EAEQ,uBAA6B;AACnC,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM;AAEX,SAAK,iBAAiB,aAAa,EAAE,QAAQ,CAAC,SAAS;AACrD,YAAM,QAAQ,KAAK,cAAc,iBAAiB;AAIlD,YAAM,WAAW,KAAK;AAAA,QACpB;AAAA,MACF;AACA,UAAI,CAAC,SAAS,CAAC,SAAU;AAEzB,YAAM,mBAAmB,MAAM,KAAK,kBAAkB,KAAK;AAE3D,YAAM,iBAAiB,SAAS,gBAAgB;AAChD,YAAM,iBAAiB,SAAS,MAAM,KAAK,gBAAgB,MAAM,EAAE,CAAC;AACpE,eAAS,iBAAiB,SAAS,MAAM;AACvC,cAAM,QAAQ;AACd,cAAM,MAAM;AACZ,yBAAiB;AACjB,aAAK,gBAAgB,MAAM,EAAE;AAAA,MAC/B,CAAC;AAED,uBAAiB;AAAA,IACnB,CAAC;AAAA,EACH;AAAA,EAEQ,kBACN,OACM;AACN,UAAM,OAAO,MAAM;AACnB,QAAI,6BAAM,UAAU,SAAS,eAAe;AAC1C,WAAK,UAAU,OAAO,aAAa,MAAM,MAAM,SAAS,CAAC;AAAA,IAC3D;AAAA,EACF;AAAA,EAEQ,cAAc,IAAY,OAAqB;AAxuBzD;AAyuBI,UAAM,MAAK,UAAK,eAAL,mBAAiB,eAAe;AAI3C,QAAI,CAAC,GAAI;AACT,OAAG,QAAQ;AACX,SAAK,kBAAkB,EAAE;AAAA,EAC3B;AAAA,EAEQ,cAAc,IAAoB;AAlvB5C;AAmvBI,UAAM,MAAK,UAAK,eAAL,mBAAiB,eAAe;AAI3C,YAAO,oCAAI,UAAJ,mBAAW,WAAX,YAAqB;AAAA,EAC9B;AAAA,EAEQ,cAAc,IAA2B;AA1vBnD;AA2vBI,UAAM,WAAU,UAAK,eAAL,mBAAiB,eAAe,GAAG,EAAE;AACrD,QAAI,SAAS;AACX,cAAQ,cAAc;AAAA,IACxB;AAEA,UAAM,SAAQ,UAAK,eAAL,mBAAiB,eAAe;AAI9C,UAAM,OAAO,+BAAO;AACpB,QAAI,6BAAM,UAAU,SAAS,eAAe;AAC1C,WAAK,UAAU,IAAI,WAAW;AAAA,IAChC;AAAA,EACF;AAAA,EAEQ,gBAAgB,IAAkB;AA1wB5C;AA2wBI,UAAM,WAAU,UAAK,eAAL,mBAAiB,eAAe,GAAG,EAAE;AACrD,QAAI,SAAS;AACX,cAAQ,cAAc;AAAA,IACxB;AAEA,UAAM,SAAQ,UAAK,eAAL,mBAAiB,eAAe;AAI9C,UAAM,OAAO,+BAAO;AACpB,QAAI,6BAAM,UAAU,SAAS,eAAe;AAC1C,WAAK,UAAU,OAAO,WAAW;AAAA,IACnC;AAAA,EACF;AAAA,EAEQ,sBAA4B;AAClC,SAAK,gBAAgB,WAAW;AAChC,SAAK,gBAAgB,cAAc;AACnC,SAAK,gBAAgB,OAAO;AAC5B,SAAK,gBAAgB,SAAS;AAAA,EAChC;AAAA,EAEQ,UACN,SACA,aACM;AApyBV;AAqyBI,UAAM,WAAU,UAAK,eAAL,mBAAiB,eAAe;AAChD,UAAM,WAAU,UAAK,eAAL,mBAAiB,eAAe;AAChD,UAAM,aAAY,UAAK,eAAL,mBAAiB,eAAe;AAClD,UAAM,aAAY,UAAK,eAAL,mBAAiB,eAAe;AAClD,UAAM,iBAAgB,UAAK,eAAL,mBAAiB,eAAe;AACtD,UAAM,aAAY,UAAK,eAAL,mBAAiB,eAAe;AAClD,UAAM,UAAS,UAAK,eAAL,mBAAiB;AAAA,MAC9B;AAAA;AAEF,UAAM,eAAc,UAAK,eAAL,mBAAiB,eAAe;AACpD,UAAM,sBAAqB,UAAK,eAAL,mBAAiB;AAAA,MAC1C;AAAA;AAEF,UAAM,kBAAiB,UAAK,eAAL,mBAAiB;AAAA,MACtC;AAAA;AAEF,QACE,CAAC,WACD,CAAC,WACD,CAAC,aACD,CAAC,aACD,CAAC,iBACD,CAAC,aACD,CAAC,UACD,CAAC,gBACD;AACA;AAAA,IACF;AAEA,SAAK,yBAAyB;AAC9B,SAAK,oBAAoB;AAEzB,YAAQ,cAAc,QAAQ;AAC9B,cAAU,cAAc,QAAQ;AAEhC,UAAM,iBAAiB,QAAQ,QAAQ,WAAW;AAClD,UAAM,SAAS,QAAQ,QAAQ,GAAG;AAClC,UAAM,cACJ,QAAQ,SAAS,cAAc,kBAAkB;AAEnD,QAAI,aAAa;AACf,oBAAc,eAAc,aAAQ,gBAAR,YAAuB;AACnD,UAAI,UAAU,QAAQ,KAAK;AACzB,eAAO,OAAO,QAAQ;AACtB,eAAO,cAAc,QAAQ;AAC7B,eAAO,QAAQ,QAAQ;AACvB,kBAAU,UAAU,OAAO,WAAW;AAAA,MACxC,OAAO;AACL,eAAO,gBAAgB,MAAM;AAC7B,eAAO,cAAc;AACrB,eAAO,gBAAgB,OAAO;AAC9B,kBAAU,UAAU,IAAI,WAAW;AAAA,MACrC;AACA,gBAAU,UAAU,OAAO,WAAW;AAAA,IACxC,OAAO;AACL,oBAAc,cAAc;AAC5B,aAAO,gBAAgB,MAAM;AAC7B,aAAO,cAAc;AACrB,aAAO,gBAAgB,OAAO;AAC9B,gBAAU,UAAU,IAAI,WAAW;AACnC,gBAAU,UAAU,IAAI,WAAW;AAAA,IACrC;AAEA,UAAM,WAAW,gBAAgB,SAC7B,cACC,QAAQ,SAAS,YAAY,KAAK,uBAAuB,KAAK;AAEnE,SAAK,mBAAmB;AACxB,SAAK,eAAe;AACpB,UAAM,eAAc,UAAK,eAAL,mBAAiB,cAAc;AACnD,SAAK,gBAAgB,QAAQ,2CAAa,QAAQ,SAAS;AAE3D,YAAQ,UAAU,OAAO,WAAW,OAAO;AAC3C,YAAQ,UAAU,IAAI,QAAQ,MAAM,MAAM;AAC1C,SAAK;AAAA,MACH,oCAAe;AAAA,MACf,kDAAsB;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,UAAU,mBAAmB,MAAY;AAt3BnD;AAu3BI,UAAM,WAAU,UAAK,eAAL,mBAAiB,eAAe;AAChD,UAAM,WAAU,UAAK,eAAL,mBAAiB,eAAe;AAChD,UAAM,aAAY,UAAK,eAAL,mBAAiB,eAAe;AAClD,UAAM,aAAY,UAAK,eAAL,mBAAiB,eAAe;AAClD,UAAM,iBAAgB,UAAK,eAAL,mBAAiB,eAAe;AACtD,UAAM,aAAY,UAAK,eAAL,mBAAiB,eAAe;AAClD,UAAM,UAAS,UAAK,eAAL,mBAAiB;AAAA,MAC9B;AAAA;AAEF,UAAM,eAAc,UAAK,eAAL,mBAAiB,eAAe;AACpD,UAAM,sBAAqB,UAAK,eAAL,mBAAiB;AAAA,MAC1C;AAAA;AAEF,UAAM,kBAAiB,UAAK,eAAL,mBAAiB;AAAA,MACtC;AAAA;AAEF,QACE,CAAC,WACD,CAAC,WACD,CAAC,aACD,CAAC,aACD,CAAC,iBACD,CAAC,aACD,CAAC,UACD,CAAC,gBACD;AACA;AAAA,IACF;AAEA,UAAM,YAAY,QAAQ,UAAU,SAAS,SAAS;AAEtD,SAAK,yBAAyB;AAC9B,SAAK,oBAAoB;AACzB,SAAK,mBAAmB;AACxB,SAAK,eAAe;AACpB,SAAK,gBAAgB;AACrB,YAAQ,cAAc;AACtB,cAAU,cAAc;AACxB,kBAAc,cAAc;AAC5B,WAAO,gBAAgB,MAAM;AAC7B,WAAO,cAAc;AACrB,WAAO,gBAAgB,OAAO;AAC9B,cAAU,UAAU,IAAI,WAAW;AACnC,cAAU,UAAU,IAAI,WAAW;AACnC,QAAI,oBAAoB;AACtB,yBAAmB,cAAc;AAAA,IACnC;AACA,mBAAe,MAAM,mBAAmB;AACxC,QAAI,aAAa;AACf,kBAAY,UAAU,IAAI,WAAW;AAAA,IACvC;AACA,YAAQ,UAAU,OAAO,WAAW,SAAS,MAAM;AAEnD,QAAI,aAAa,kBAAkB;AACjC,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA,EAEQ,oBACN,aACA,WACA,OACM;AACN,UAAM,kBAAkB,MAAY;AAClC,YAAM,UAAU,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,mBAAmB,GAAI,CAAC;AACnE,UAAI,WAAW;AACb,kBAAU,cAAc,GAAG,OAAO;AAAA,MACpC;AAEA,YAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,mBAAmB,KAAK,YAAY,CAAC;AAClF,YAAM,SAAS,SAAS,IAAI;AAC5B,YAAM,MAAM,mBAAmB,GAAG,MAAM;AAAA,IAC1C;AAEA,oBAAgB;AAChB,QAAI,aAAa;AACf,kBAAY,UAAU,OAAO,WAAW;AAAA,IAC1C;AAEA,SAAK,yBAAyB,OAAO,YAAY,MAAM;AACrD,UAAI,KAAK,cAAe;AAExB,WAAK,oBAAoB;AACzB,UAAI,KAAK,oBAAoB,GAAG;AAC9B,aAAK,mBAAmB;AACxB,cAAM,MAAM,mBAAmB;AAC/B,YAAI,WAAW;AACb,oBAAU,cAAc;AAAA,QAC1B;AACA,aAAK,UAAU;AACf;AAAA,MACF;AACA,sBAAgB;AAAA,IAClB,GAAG,GAAG;AAAA,EACR;AAAA,EAEQ,sBAA4B;AAClC,QAAI,KAAK,2BAA2B,MAAM;AACxC,aAAO,cAAc,KAAK,sBAAsB;AAChD,WAAK,yBAAyB;AAAA,IAChC;AAAA,EACF;AAAA,EAEQ,eAAe,SAA2C;AAChE,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OACE,QAAQ,SAAS,YACb,8CACA;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,2BAAiC;AACvC,QAAI,KAAK,wBAAwB,MAAM;AACrC,aAAO,aAAa,KAAK,mBAAmB;AAC5C,WAAK,sBAAsB;AAAA,IAC7B;AAAA,EACF;AAAA,EAEQ,uBAAuB,OAAuB;AACpD,QAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG;AACzC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,MAAM,KAAK;AAAA,EACzB;AAAA,EAEQ,8BAAoC;AAC1C,QAAI,KAAK,2BAA2B,MAAM;AACxC,aAAO,aAAa,KAAK,sBAAsB;AAC/C,WAAK,yBAAyB;AAAA,IAChC;AAAA,EACF;AAAA,EAEQ,yBAAkC;AA7/B5C;AA8/BI,SAAK,oBAAoB;AAEzB,UAAM,iBAAoC;AAAA,MACxC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,iBAAyC;AAE7C,mBAAe,QAAQ,CAAC,OAAO;AAC7B,UAAI,CAAC,KAAK,cAAc,EAAE,GAAG;AAC3B,aAAK,cAAc,EAAE;AACrB,YAAI,CAAC,gBAAgB;AACnB,2BAAiB;AAAA,QACnB;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,gBAAgB;AAClB,YAAM,gBAAe,UAAK,eAAL,mBAAiB,eAAe;AAIrD,mDAAc;AACd,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,gBAAsB;AAC5B,SAAK,cAAc,aAAa,EAAE;AAClC,SAAK,cAAc,SAAS,EAAE;AAC9B,SAAK,cAAc,gBAAgB,EAAE;AACrC,SAAK,cAAc,WAAW,EAAE;AAChC,SAAK,oBAAoB;AAEzB,SAAK,gBAAgB,CAAC;AACtB,SAAK,uBAAuB,CAAC;AAC7B,SAAK,eAAe;AACpB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAc,SAAwB;AA3iCxC;AA4iCI,SAAK,4BAA4B;AACjC,SAAK,UAAU,KAAK;AAEpB,QAAI,CAAC,KAAK,uBAAuB,GAAG;AAClC,WAAK;AAAA,QACH,IAAI,YAAY,cAAc,cAAc;AAAA,UAC1C,SAAS;AAAA,UACT,QAAQ,EAAE,SAAS,sCAAsC;AAAA,QAC3D,CAAC;AAAA,MACH;AACA;AAAA,IACF;AAEA,UAAM,OAAoB;AAAA,MACxB,MAAM,KAAK,cAAc,WAAW;AAAA,MACpC,OAAO,KAAK,cAAc,OAAO;AAAA,MACjC,aAAa,KAAK,cAAc,cAAc;AAAA,IAChD;AAEA,SAAK,OAAO;AAEZ,QAAI;AACF,2BAAqB,KAAK,MAAM;AAChC,mBAAa,IAAI;AAEjB,YAAMC,WAAU,KAAK,cAAc,SAAS;AAC5C,YAAM,aAAqC;AAAA,QACzC,SAAAA;AAAA,QACA,aAAa,CAAC;AAAA;AAAA,MAChB;AACA,yBAAmB,UAAU;AAAA,IAC/B,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,WAAK;AAAA,QACH,IAAI,YAAY,cAAc,cAAc;AAAA,UAC1C,SAAS;AAAA,UACT,QAAQ,EAAE,QAAQ;AAAA,QACpB,CAAC;AAAA,MACH;AACA;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,cAAc,SAAS;AAG5C,UAAM,WAAW,IAAI,SAAS;AAC9B,aAAS,OAAO,aAAa,KAAK,IAAI;AACtC,aAAS,OAAO,eAAe,KAAK,WAAW;AAC/C,aAAS,OAAO,SAAS,KAAK,KAAK;AACnC,aAAS,OAAO,WAAW,OAAO;AAClC,SAAI,UAAK,OAAO,cAAZ,mBAAuB,QAAQ;AACjC,eAAS,OAAO,aAAa,KAAK,OAAO,UAAU,KAAK,CAAC;AAAA,IAC3D;AACA,QAAI,OAAO,KAAK,aAAa,UAAU;AACrC,eAAS,OAAO,YAAY,OAAO,KAAK,QAAQ,CAAC;AAAA,IACnD;AAGA,eAAW,OAAO,KAAK,sBAAsB;AAC3C,YAAM,OAAO,MAAM,UAAU,GAAG;AAChC,UAAI,MAAM;AACR,iBAAS,OAAO,eAAe,IAAI;AAAA,MACrC;AAAA,IACF;AAGA,eAAW,QAAQ,KAAK,eAAe;AACrC,eAAS,OAAO,eAAe,IAAI;AAAA,IACrC;AAGA,QAAI,KAAK,aAAa,SAAS,GAAG;AAChC,WAAK,aAAa;AAAA,QAAQ,CAAC,UACzB,SAAS,OAAO,gBAAgB,KAAK;AAAA,MACvC;AAAA,IACF;AACA,QAAI,KAAK,cAAc,SAAS,GAAG;AACjC,WAAK,cAAc;AAAA,QAAQ,CAAC,YAC1B,SAAS,OAAO,iBAAiB,OAAO;AAAA,MAC1C;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,eAAe,KAAK,MAAM;AAC1C,YAAM,SAAS,MAAM,qBAAqB,SAAS,QAAQ;AAE3D,UAAI,CAAC,uBAAuB,MAAM,GAAG;AACnC,cAAM,iBAAiB,wBAAwB,MAAM;AACrD,aAAK,UAAU,KAAK,eAAe,cAAc,CAAC;AAClD,aAAK;AAAA,UACH,IAAI,YAAY,cAAc,cAAc;AAAA,YAC1C,SAAS;AAAA,YACT,QAAQ,EAAE,SAAS,eAAe,SAAS,UAAU,OAAO;AAAA,UAC9D,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAEA,YAAM,iBAAiB,wBAAwB,MAAM;AACrD,WAAK,UAAU,KAAK,eAAe,cAAc,CAAC;AAClD,WAAK;AAAA,QACH,IAAI,YAAY,cAAc,gBAAgB;AAAA,UAC5C,SAAS;AAAA,UACT,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AACA,WAAK,yBAAyB,OAAO,WAAW,MAAM;AACpD,aAAK,UAAU;AAAA,MACjB,GAAG,KAAK,oBAAoB;AAAA,IAC9B,SAAS,OAAO;AACd,YAAM,eAAe,mBAAmB,KAAK;AAC7C,WAAK,UAAU,KAAK,eAAe,YAAY,CAAC;AAChD,WAAK;AAAA,QACH,IAAI,YAAY,cAAc,cAAc;AAAA,UAC1C,SAAS;AAAA,UACT,QAAQ,EAAE,SAAS,aAAa,QAAQ;AAAA,QAC1C,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,UAAU,UAA6B;AAC7C,aAAS,iBAAiB,eAAe,CAAC,MAAoB;AAC5D,UAAI,EAAE,WAAW,EAAG;AAEpB,WAAK,eAAe;AACpB,YAAM,OAAO,KAAK,sBAAsB;AACxC,YAAM,SAAS,EAAE;AACjB,YAAM,SAAS,EAAE;AACjB,YAAM,cAAc,KAAK;AACzB,YAAM,aAAa,KAAK;AAExB,UAAI;AACF,iBAAS,kBAAkB,EAAE,SAAS;AAAA,MACxC,SAAQC,IAAA;AAAA,MAER;AAEA,YAAM,gBAAgB,CAAC,cAA4B;AACjD,cAAM,KAAK,UAAU,UAAU;AAC/B,cAAM,KAAK,UAAU,UAAU;AAE/B,YAAI,CAAC,KAAK,iBAAiB,KAAK,IAAI,EAAE,IAAI,KAAK,KAAK,IAAI,EAAE,IAAI,IAAI;AAChE,eAAK,eAAe;AACpB,mBAAS,MAAM,SAAS;AAAA,QAC1B;AAEA,YAAI,KAAK,cAAc;AACrB,gBAAM,UAAU,cAAc;AAC9B,gBAAM,SAAS,aAAa;AAC5B,eAAK,eAAe,SAAS,MAAM;AAAA,QACrC;AAAA,MACF;AAEA,YAAM,cAAc,CAAC,YAA0B;AAC7C,YAAI;AACF,mBAAS,sBAAsB,QAAQ,SAAS;AAAA,QAClD,SAAQA,IAAA;AAAA,QAER;AACA,iBAAS,oBAAoB,eAAe,aAAa;AACzD,iBAAS,oBAAoB,aAAa,WAAW;AACrD,iBAAS,oBAAoB,iBAAiB,WAAW;AACzD,iBAAS,MAAM,SAAS;AAExB,YAAI,KAAK,cAAc;AACrB,uBAAa;AAAA,YACX;AAAA,YACA,KAAK,UAAU;AAAA,cACb,MAAM,KAAK,MAAM;AAAA,cACjB,KAAK,KAAK,MAAM;AAAA,YAClB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAEA,eAAS,iBAAiB,eAAe,aAAa;AACtD,eAAS,iBAAiB,aAAa,WAAW;AAClD,eAAS,iBAAiB,iBAAiB,WAAW;AAAA,IACxD,CAAC;AAAA,EACH;AAAA,EAEQ,eAAe,SAAiB,QAAsB;AAC5D,UAAM,OAAO,KAAK,sBAAsB;AACxC,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,SAAS,KAAK,UAAU;AAE9B,UAAM,UAAU;AAChB,UAAM,UAAU,OAAO,aAAa;AACpC,UAAM,SAAS;AACf,UAAM,SAAS,OAAO,cAAc;AAEpC,UAAM,cAAc,KAAK,IAAI,SAAS,KAAK,IAAI,SAAS,OAAO,CAAC;AAChE,UAAM,aAAa,KAAK,IAAI,QAAQ,KAAK,IAAI,QAAQ,MAAM,CAAC;AAE5D,SAAK,MAAM,QAAQ;AACnB,SAAK,MAAM,SAAS;AACpB,SAAK,MAAM,OAAO,GAAG,WAAW;AAChC,SAAK,MAAM,MAAM,GAAG,UAAU;AAAA,EAChC;AAAA,EAEQ,kBAAwB;AAC9B,UAAM,gBAAgB,aAAa,QAAQ,wBAAwB;AACnE,QAAI,eAAe;AACjB,UAAI;AACF,cAAM,EAAE,MAAM,IAAI,IAAI,KAAK,MAAM,aAAa;AAC9C,YAAI,QAAQ,KAAK;AACf,gBAAM,UAAU,WAAW,IAAI;AAC/B,gBAAM,SAAS,WAAW,GAAG;AAC7B,eAAK,eAAe,SAAS,MAAM;AAAA,QACrC;AAAA,MACF,SAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AASF;AAjtCa,qBACJ,qBAAqB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACpEF,IAAM,WAAW;AAEV,SAAS,wBAA8B;AAC5C,MAAI,CAAC,eAAe,IAAI,QAAQ,GAAG;AACjC,mBAAe,OAAO,UAAU,oBAAoB;AAAA,EACtD;AACF;AAEA,sBAAsB;","names":["_a","content","e"]}
|
package/dist/index.cjs
CHANGED
|
@@ -195,7 +195,8 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
195
195
|
this.closeAfterSuccessTimer = null;
|
|
196
196
|
this.toastAutoCloseTimer = null;
|
|
197
197
|
this.toastCountdownInterval = null;
|
|
198
|
-
this.
|
|
198
|
+
this.toastSuccessDuration = 4e3;
|
|
199
|
+
this.toastErrorDuration = 4e3;
|
|
199
200
|
this.toastRemainingMs = 0;
|
|
200
201
|
this.toastTotalMs = 0;
|
|
201
202
|
this.isToastPaused = false;
|
|
@@ -261,7 +262,23 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
261
262
|
this.emailContacts = config.emailContacts;
|
|
262
263
|
}
|
|
263
264
|
if (config.toastDuration !== void 0) {
|
|
264
|
-
|
|
265
|
+
const duration = config.toastDuration;
|
|
266
|
+
if (typeof duration === "number") {
|
|
267
|
+
const val = this.normalizeToastDuration(duration);
|
|
268
|
+
this.toastSuccessDuration = val;
|
|
269
|
+
this.toastErrorDuration = val;
|
|
270
|
+
} else if (duration && typeof duration === "object") {
|
|
271
|
+
if (duration.success !== void 0) {
|
|
272
|
+
this.toastSuccessDuration = this.normalizeToastDuration(duration.success);
|
|
273
|
+
} else {
|
|
274
|
+
this.toastSuccessDuration = 4e3;
|
|
275
|
+
}
|
|
276
|
+
if (duration.error !== void 0) {
|
|
277
|
+
this.toastErrorDuration = this.normalizeToastDuration(duration.error);
|
|
278
|
+
} else {
|
|
279
|
+
this.toastErrorDuration = 4e3;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
265
282
|
}
|
|
266
283
|
}
|
|
267
284
|
setFormPrefill(data) {
|
|
@@ -279,6 +296,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
279
296
|
var _a, _b;
|
|
280
297
|
this.clearCloseAfterSuccessTimer();
|
|
281
298
|
this.resetFormData();
|
|
299
|
+
this.hideToast(false);
|
|
282
300
|
(_b = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("modal")) == null ? void 0 : _b.classList.remove("show");
|
|
283
301
|
this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.CLOSE, { bubbles: true }));
|
|
284
302
|
}
|
|
@@ -308,9 +326,27 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
308
326
|
};
|
|
309
327
|
const toastDurationAttr = this.getAttribute("toast-duration");
|
|
310
328
|
if (toastDurationAttr !== null) {
|
|
311
|
-
const
|
|
312
|
-
if (
|
|
313
|
-
|
|
329
|
+
const trimmed = toastDurationAttr.trim();
|
|
330
|
+
if (trimmed.startsWith("{")) {
|
|
331
|
+
try {
|
|
332
|
+
const parsed = JSON.parse(trimmed);
|
|
333
|
+
if (parsed && typeof parsed === "object") {
|
|
334
|
+
if (parsed.success !== void 0) {
|
|
335
|
+
this.toastSuccessDuration = this.normalizeToastDuration(Number(parsed.success));
|
|
336
|
+
}
|
|
337
|
+
if (parsed.error !== void 0) {
|
|
338
|
+
this.toastErrorDuration = this.normalizeToastDuration(Number(parsed.error));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} catch (e) {
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
const parsed = parseInt(trimmed, 10);
|
|
345
|
+
if (!isNaN(parsed)) {
|
|
346
|
+
const val = this.normalizeToastDuration(parsed);
|
|
347
|
+
this.toastSuccessDuration = val;
|
|
348
|
+
this.toastErrorDuration = val;
|
|
349
|
+
}
|
|
314
350
|
}
|
|
315
351
|
}
|
|
316
352
|
}
|
|
@@ -751,7 +787,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
751
787
|
this.clearFieldError("email");
|
|
752
788
|
this.clearFieldError("content");
|
|
753
789
|
}
|
|
754
|
-
showToast(payload, autoCloseMs
|
|
790
|
+
showToast(payload, autoCloseMs) {
|
|
755
791
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
756
792
|
const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
|
|
757
793
|
const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
|
|
@@ -801,8 +837,9 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
801
837
|
linkRowEl.classList.add("is-hidden");
|
|
802
838
|
detailsEl.classList.add("is-hidden");
|
|
803
839
|
}
|
|
804
|
-
this.
|
|
805
|
-
this.
|
|
840
|
+
const duration = autoCloseMs !== void 0 ? autoCloseMs : payload.type === "success" ? this.toastSuccessDuration : this.toastErrorDuration;
|
|
841
|
+
this.toastRemainingMs = duration;
|
|
842
|
+
this.toastTotalMs = duration;
|
|
806
843
|
const toastCardEl = (_l = this.shadowRoot) == null ? void 0 : _l.querySelector(".toast-card");
|
|
807
844
|
this.isToastPaused = Boolean(toastCardEl == null ? void 0 : toastCardEl.matches(":hover"));
|
|
808
845
|
toastEl.classList.remove("success", "error");
|
|
@@ -813,7 +850,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
813
850
|
countdownBarEl
|
|
814
851
|
);
|
|
815
852
|
}
|
|
816
|
-
hideToast() {
|
|
853
|
+
hideToast(shouldCloseModal = true) {
|
|
817
854
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
818
855
|
const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
|
|
819
856
|
const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
|
|
@@ -834,6 +871,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
834
871
|
if (!toastEl || !titleEl || !messageEl || !detailsEl || !requestCodeEl || !linkRowEl || !linkEl || !countdownBarEl) {
|
|
835
872
|
return;
|
|
836
873
|
}
|
|
874
|
+
const isSuccess = toastEl.classList.contains("success");
|
|
837
875
|
this.clearToastAutoCloseTimer();
|
|
838
876
|
this.clearToastCountdown();
|
|
839
877
|
this.toastRemainingMs = 0;
|
|
@@ -855,6 +893,9 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
855
893
|
countdownEl.classList.add("is-hidden");
|
|
856
894
|
}
|
|
857
895
|
toastEl.classList.remove("success", "error", "show");
|
|
896
|
+
if (isSuccess && shouldCloseModal) {
|
|
897
|
+
this.close();
|
|
898
|
+
}
|
|
858
899
|
}
|
|
859
900
|
startToastCountdown(countdownEl, secondsEl, barEl) {
|
|
860
901
|
const updateCountdown = () => {
|
|
@@ -954,7 +995,7 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
954
995
|
async submit() {
|
|
955
996
|
var _a;
|
|
956
997
|
this.clearCloseAfterSuccessTimer();
|
|
957
|
-
this.hideToast();
|
|
998
|
+
this.hideToast(false);
|
|
958
999
|
if (!this.validateRequiredFields()) {
|
|
959
1000
|
this.dispatchEvent(
|
|
960
1001
|
new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {
|
|
@@ -1044,8 +1085,8 @@ var SupportWidgetElement = class extends HTMLElement {
|
|
|
1044
1085
|
})
|
|
1045
1086
|
);
|
|
1046
1087
|
this.closeAfterSuccessTimer = window.setTimeout(() => {
|
|
1047
|
-
this.
|
|
1048
|
-
},
|
|
1088
|
+
this.hideToast();
|
|
1089
|
+
}, this.toastSuccessDuration);
|
|
1049
1090
|
} catch (error) {
|
|
1050
1091
|
const errorContent = mapHttpClientError(error);
|
|
1051
1092
|
this.showToast(this.toToastPayload(errorContent));
|