@namphuongtechnologi/np-hub 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core/config/endpoints.ts","../src/core/validation/request-validator.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 { registerSupportWidget } from \"./register\";\nexport { SupportWidgetElement } from \"./component/support-widget.element\";\nexport { WIDGET_EVENTS } from \"./component/events/widget-events\";\nexport type {\n SupportRequestPayload,\n SupportSubmissionInput,\n SupportUser,\n SupportWidgetConfig\n} from \"./types/public\";\n","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 || config.projectId.trim().length === 0) {\n throw new Error(\"projectId is required.\");\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 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 { postFormData } from \"./http-client\";\n\ninterface CreateRequestResponse {\n statusCode?: number;\n message?: string;\n data?: {\n url?: string;\n requestCode?: string;\n };\n}\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: 20px;\n bottom: 20px;\n z-index: 10000;\n}\n\n.launcher {\n width: var(--np-hub-width, 72px);\n height: var(--np-hub-height, 72px);\n border: none;\n border-radius: 50%;\n cursor: grab;\n box-shadow: 0 8px 28px rgba(0, 60, 130, 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 32px 32px;\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: 18px;\n margin-bottom: 22px;\n border-bottom: 1px solid var(--np-border);\n position: relative;\n}\n\n.title {\n margin: 0;\n font-size: 22px;\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: 18px;\n}\n\n.field {\n display: flex;\n flex-direction: column;\n gap: 7px;\n}\n\n.field.full {\n grid-column: 1 / -1;\n}\n\nlabel {\n font-size: 13.5px;\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: 12px 14px;\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: 120px;\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 {\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 20px;\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: 14px;\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: 28px;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n}\n\n.btn {\n border-radius: 10px;\n padding: 12px 26px;\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</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 </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 </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 </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 </div>\n\n <div class=\"field full\">\n <label>File đính kèm</label>\n <div id=\"upload-trigger\" class=\"upload-zone\" role=\"button\" tabindex=\"0\" aria-label=\"Tải lên tệp\">\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\">Chọn tệp tải lên hoặc kéo thả vào đây</span>\n <span class=\"upload-sub-text\">Hỗ trợ dán ảnh hoặc tệp (Ctrl+V / Cmd+V) để đính kèm nhanh</span>\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","import { resolveBaseUrl } from \"../core/config/endpoints\";\nimport {\n validateSubmission,\n validateUser,\n validateWidgetConfig,\n} from \"../core/validation/request-validator\";\nimport { createSupportRequest } from \"../services/api/support-api\";\nimport type {\n SupportSubmissionInput,\n SupportUser,\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 priority?: number;\n coordinators?: string[];\n emailContacts?: string[];\n}\n\nexport class SupportWidgetElement extends HTMLElement {\n static observedAttributes = [\"project-id\", \"is-dev\", \"width\", \"height\"];\n\n private config: SupportWidgetConfig = {\n projectId: \"\",\n };\n\n private user?: SupportUser;\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 priority = 0;\n private coordinators: string[] = [];\n private 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.restorePosition();\n window.addEventListener(\"resize\", this.handleResize);\n }\n\n disconnectedCallback(): void {\n window.removeEventListener(\"resize\", this.handleResize);\n }\n\n attributeChangedCallback(): void {\n this.syncConfigFromAttributes();\n this.prefillFromState();\n this.applyLauncherStyle();\n }\n\n setUser(user: SupportUser): void {\n this.user = user;\n this.prefillFromState();\n }\n\n setFormPrefill(data: FormPrefillInput): void {\n this.setInputValue(\"content\", data.content ?? \"\");\n this.prefilledAttachments = data.attachments ?? [];\n this.priority = data.priority ?? 0;\n this.coordinators = data.coordinators ?? [];\n this.emailContacts = data.emailContacts ?? [];\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.shadowRoot?.getElementById(\"modal\")?.classList.remove(\"show\");\n this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.CLOSE, { bubbles: true }));\n }\n\n private syncConfigFromAttributes(): void {\n this.config = {\n projectId: this.getAttribute(\"project-id\") ?? \"\",\n isDev: this.hasAttribute(\"is-dev\"),\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\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.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 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 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 clearBtn.addEventListener(\"click\", () => {\n input.value = \"\";\n input.focus();\n updateVisibility();\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 async submit(): Promise<void> {\n try {\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 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 priority: this.priority,\n coordinators: this.coordinators,\n emailContacts: this.emailContacts,\n };\n validateSubmission(submission);\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 formData.append(\"ProjectId\", this.config.projectId);\n formData.append(\"Priority\", String(this.priority));\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 this.coordinators.forEach((coord) =>\n formData.append(\"Coordinators\", coord),\n );\n this.emailContacts.forEach((contact) =>\n formData.append(\"EmailContacts\", contact),\n );\n\n const baseUrl = resolveBaseUrl(this.config);\n const result = await createSupportRequest(baseUrl, formData);\n\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_SUCCESS, {\n bubbles: true,\n detail: result,\n }),\n );\n this.close();\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {\n bubbles: true,\n detail: { 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":"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","names":[]}
@@ -0,0 +1,89 @@
1
+ export { registerSupportWidget } from './register.cjs';
2
+
3
+ interface SupportUser {
4
+ name: string;
5
+ email: string;
6
+ phoneNumber: string;
7
+ }
8
+ interface SupportWidgetConfig {
9
+ projectId: string;
10
+ isDev?: boolean;
11
+ }
12
+ interface SupportSubmissionInput {
13
+ content: string;
14
+ attachments: string[];
15
+ priority?: number;
16
+ coordinators?: string[];
17
+ emailContacts?: string[];
18
+ }
19
+ interface SupportRequestPayload {
20
+ Requester: string;
21
+ PhoneNumber: string;
22
+ Email: string;
23
+ Content: string;
24
+ ProjectId: string;
25
+ Priority: number;
26
+ Attachments: string[];
27
+ Coordinators: string[];
28
+ EmailContacts: string[];
29
+ }
30
+
31
+ interface FormPrefillInput {
32
+ content?: string;
33
+ attachments?: string[];
34
+ priority?: number;
35
+ coordinators?: string[];
36
+ emailContacts?: string[];
37
+ }
38
+ declare class SupportWidgetElement extends HTMLElement {
39
+ static observedAttributes: string[];
40
+ private config;
41
+ private user?;
42
+ private dragOccurred;
43
+ private selectedFiles;
44
+ private prefilledAttachments;
45
+ private previewUrls;
46
+ private priority;
47
+ private coordinators;
48
+ private emailContacts;
49
+ constructor();
50
+ connectedCallback(): void;
51
+ disconnectedCallback(): void;
52
+ attributeChangedCallback(): void;
53
+ setUser(user: SupportUser): void;
54
+ setFormPrefill(data: FormPrefillInput): void;
55
+ open(): void;
56
+ close(): void;
57
+ private syncConfigFromAttributes;
58
+ private bindActions;
59
+ private setupPreviewControls;
60
+ private setupClipboardPaste;
61
+ private addSelectedFiles;
62
+ private renderFileList;
63
+ private buildFileItem;
64
+ private fillWithFileIcon;
65
+ private openPreview;
66
+ private closePreview;
67
+ private isImageName;
68
+ private prefillFromState;
69
+ private applyLauncherStyle;
70
+ private normalizeSize;
71
+ private setupClearableInputs;
72
+ private updateClearButton;
73
+ private setInputValue;
74
+ private getInputValue;
75
+ private submit;
76
+ private setupDrag;
77
+ private adjustPosition;
78
+ private restorePosition;
79
+ private handleResize;
80
+ }
81
+
82
+ declare const WIDGET_EVENTS: {
83
+ readonly OPEN: "np-hub-open";
84
+ readonly CLOSE: "np-hub-close";
85
+ readonly SUBMIT_SUCCESS: "np-hub-submit-success";
86
+ readonly SUBMIT_ERROR: "np-hub-submit-error";
87
+ };
88
+
89
+ export { type SupportRequestPayload, type SupportSubmissionInput, type SupportUser, type SupportWidgetConfig, SupportWidgetElement, WIDGET_EVENTS };
@@ -0,0 +1,89 @@
1
+ export { registerSupportWidget } from './register.js';
2
+
3
+ interface SupportUser {
4
+ name: string;
5
+ email: string;
6
+ phoneNumber: string;
7
+ }
8
+ interface SupportWidgetConfig {
9
+ projectId: string;
10
+ isDev?: boolean;
11
+ }
12
+ interface SupportSubmissionInput {
13
+ content: string;
14
+ attachments: string[];
15
+ priority?: number;
16
+ coordinators?: string[];
17
+ emailContacts?: string[];
18
+ }
19
+ interface SupportRequestPayload {
20
+ Requester: string;
21
+ PhoneNumber: string;
22
+ Email: string;
23
+ Content: string;
24
+ ProjectId: string;
25
+ Priority: number;
26
+ Attachments: string[];
27
+ Coordinators: string[];
28
+ EmailContacts: string[];
29
+ }
30
+
31
+ interface FormPrefillInput {
32
+ content?: string;
33
+ attachments?: string[];
34
+ priority?: number;
35
+ coordinators?: string[];
36
+ emailContacts?: string[];
37
+ }
38
+ declare class SupportWidgetElement extends HTMLElement {
39
+ static observedAttributes: string[];
40
+ private config;
41
+ private user?;
42
+ private dragOccurred;
43
+ private selectedFiles;
44
+ private prefilledAttachments;
45
+ private previewUrls;
46
+ private priority;
47
+ private coordinators;
48
+ private emailContacts;
49
+ constructor();
50
+ connectedCallback(): void;
51
+ disconnectedCallback(): void;
52
+ attributeChangedCallback(): void;
53
+ setUser(user: SupportUser): void;
54
+ setFormPrefill(data: FormPrefillInput): void;
55
+ open(): void;
56
+ close(): void;
57
+ private syncConfigFromAttributes;
58
+ private bindActions;
59
+ private setupPreviewControls;
60
+ private setupClipboardPaste;
61
+ private addSelectedFiles;
62
+ private renderFileList;
63
+ private buildFileItem;
64
+ private fillWithFileIcon;
65
+ private openPreview;
66
+ private closePreview;
67
+ private isImageName;
68
+ private prefillFromState;
69
+ private applyLauncherStyle;
70
+ private normalizeSize;
71
+ private setupClearableInputs;
72
+ private updateClearButton;
73
+ private setInputValue;
74
+ private getInputValue;
75
+ private submit;
76
+ private setupDrag;
77
+ private adjustPosition;
78
+ private restorePosition;
79
+ private handleResize;
80
+ }
81
+
82
+ declare const WIDGET_EVENTS: {
83
+ readonly OPEN: "np-hub-open";
84
+ readonly CLOSE: "np-hub-close";
85
+ readonly SUBMIT_SUCCESS: "np-hub-submit-success";
86
+ readonly SUBMIT_ERROR: "np-hub-submit-error";
87
+ };
88
+
89
+ export { type SupportRequestPayload, type SupportSubmissionInput, type SupportUser, type SupportWidgetConfig, SupportWidgetElement, WIDGET_EVENTS };
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import {
2
+ SupportWidgetElement,
3
+ WIDGET_EVENTS,
4
+ registerSupportWidget
5
+ } from "./chunk-ZZ63ZHC7.js";
6
+ export {
7
+ SupportWidgetElement,
8
+ WIDGET_EVENTS,
9
+ registerSupportWidget
10
+ };
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}