diff --git a/language/en-au/res.json b/language/en-au/res.json new file mode 100644 index 0000000..eff016e --- /dev/null +++ b/language/en-au/res.json @@ -0,0 +1,211 @@ +{ + "ver": "#{$X-Res-Ver}", + "title": "Fleet Intelligence\u2122 Internal", + "logout": "Logout", + "workOrder": "Work Orders You Are Following", + "periodCol": "Period:", + "openedDateCol": "Opened Date:", + "closedDateCol": "Closed Date:", + "to": "to", + "threeMonths": "Within three months", + "thisYear": "This year", + "lastYear": "Last Year", + "all": "All", + "allOpened": "All Opened", + "thisMonth": "This Month", + "lastMonth": "Last Month", + "thisQuarter": "This Quarter", + "lastQuarter": "Last Quarter", + "yearToDate": "Year To Date", + "custom": "Custom", + "refresh": "Refresh", + "exportExcel": "Export to Excel", + "report": "Report", + "allItem": "( All )", + "ok": "OK", + "reset": "Reset", + "yes": "Yes", + "no": "No", + "unknownError": "An unknown error occurred, please contact the administrator.", + "searchHolder": "Search...", + "workOrderNumber": "Work Order Number", + "workOrderNumberWidth": 170, + "workOrderType": "Work Order Type", + "workOrderTypeWidth": 190, + "assignedTo": "Assigned To", + "assignedToWidth": 190, + "dealerName": "Dealer Name", + "dealerNameWidth": 270, + "status": "Status", + "statusWidth": 180, + "createDate": "Created Date", + "createDateWidth": 130, + "completeDate": "Completed Date", + "completeDateWidth": 130, + "completed": "Completed", + "totalCost": "Total Cost", + "totalCostWidth": 100, + "vin": "VIN", + "vinWidth": 150, + "make": "Make", + "makeColon": "Make: ", + "makeWidth": 150, + "model": "Model", + "modelColon": "Model:", + "modelWidth": 150, + "type": "Type", + "typeWidth": 150, + "customerCode": "Customer Code", + "customerWidth": 180, + "userProfile": "User Profile", + "save": "Save", + "cancel": "Cancel", + "userName": "Display Name", + "mobilePhone": "Mobile Phone", + "timeZone": "Time Zone", + "language": "Language", + "companyName": "Company Name", + "companyNameWidth": 270, + "advisor": "Advisor", + "advisorWidth": 120, + "lastCommunicationText": "Last Communication Text", + "lastCommunicationTextWidth": 320, + "lastCommunicationBy": "Last Communication By", + "lastCommunicationByWidth": 190, + "lastCommunicationDate": "Last Communication Date", + "lastCommunicationDateWidth": 190, + "displayClosed": "Display Closed (90 days)", + "companyNameColon": "Company Name:", + "woNumberColon": "WO#:", + "savedSuccessfully": "Saved successfully.", + "viewWorkOrder": "Work Order - {0}", + "informationonAssetColon": "Information on Asset: ", + "workOrderColon": "Work Order: ", + "statusColon": "Status:", + "billingInformation": "Billing Information", + "estimateTotalAmountColon": "Estimate Total Amount: ", + "notesColon": "Notes: ", + "issuesIdentifiedinInspection": "Issues Identified in Inspection", + "identifiedIssues": "IDENTIFIED ISSUES", + "inspectionDetail": "INSPECTION DETAIL", + "noIdentifieds": "No Issues Identified in Inspection", + "severityLevelColon": "Severity Level: ", + "low": "Low", + "medium": "Medium", + "high": "High", + "noQuestions": "No Questions", + "workOrderAttachments": "Work Order Attachments", + "messages": "Customer Communication", + "internalComments": "Internal Comments", + "sentTo": "Sent To :", + "sent": "Sent", + "undelivered": "Undelivered", + "delivered": "Delivered", + "failed": "Failed", + "review": "Review", + "pending": "Pending", + "optOut": "Opt-Out", + "landline": "Landline", + "confirmSendMessage": "Message will be sent to:", + "confirmContinue": "Do you want to continue?", + "send": "Send", + "viewWorkOrderInspection": "View Work Order Inspection - {0}", + "viewFullInspection": "View Full Inspection", + "summary": "Summary", + "welcomeTitle": "Thank you for your business!", + "workOrderNumberColon": "Work Order Number:", + "openDateColon": "Open Date:", + "completedDateColon": "Completed Date:", + "approveRejectEstimate": "Approve/Reject Estimate", + "partsCostColon": "Parts Cost:", + "laborCostColon": "Labor Cost:", + "travelTimeCostColon": "Travel Time Cost:", + "otherCostColon": "Other Cost:", + "assetDetail": "Asset Detail", + "assetColon": "Asset:", + "hourMeterColon": "Hour Meter:", + "vinColon": "VIN:", + "typeColon": "Type:", + "odometerColon": "Odometer:", + "workOrderDetail": "Work Order Detail", + "viewApproveEstimate": "View/Approve Estimate", + "workOrderTypeColon": "Work Order Type:", + "customerReportedIssuesColon": "Issues:", + "workOrderTotalCostColon": "Work Order Total Cost:", + "estimateTotalCostColon": "Estimate Total Cost:", + "timeToCompleteColon": "Time To Complete(Hrs):", + "estimateDetail": "Estimate Detail", + "notesFromCustomerColon": "Notes from Customer:", + "approve": "Approve", + "reject": "Reject", + "approveEstimate": "Approve Estimate", + "rejectEstimate": "Reject Estimate", + "confirmApproveEstimate": "Are you sure you want to approve the estimate?", + "confirmRejectEstimate": "Are you sure you want to reject the estimate?", + "estimateStatusColon": "Estimate Status:", + "technicianNotesColon": "Technician Notes:", + "draft": "Draft", + "awaitingResponse": "Awaiting Response", + "closedByDealer": "Closed By Dealer", + "customerRejected": "Customer Rejected", + "customerApproved": "Customer Approved", + "estimateAttachments": "Estimate Attachments", + "photoDocuments": "Photos and Documentation", + "estimateNotPrepared": "Estimate Not Prepared", + "notAvailable": "N/A", + "signatureColon": "Signature:", + "signature": "Signature", + "requireSignature": "Please make a signature first.", + "clear": "Clear", + "approvedByColon": "Approved By:", + "phoneNumberColon": "Phone Number:", + "requireApprovedBy": "Please input the approver.", + "requirePhoneNumber": "Please input the phone number.", + "willWorkOurselves": "Will perform work ourselves", + "willWorkElsewhere": "Will have work done elsewhere", + "cost": "Cost", + "willNotWork": "Will not have work done", + "willWorkLater": "Will have work done at a later date", + "other": "Other", + "preferNotAnswer": "Prefer Not to Answer", + "reasonColon": "Reason:", + "termOfUse": "Terms of Use", + "requireReason": "Please select or input a reason.", + "submit": "Submit", + "survey": "Survey", + "thanksSurvey": "Your survey has been submitted successfully. Thank you for taking the survey.", + "surveyPrompt": "Please Rate the services that you received on work order {wonum} for your {make}/{model}", + "invoiceDetail": "Invoice Detail", + "awaitingPayment": "Awaiting Payment", + "paymentFailure": "Payment Failure", + "customerPaid": "Paid", + "invoiceStatusColon": "Invoice Status:", + "invoiceTotalCostColon": "Invoice Total Cost:", + "taxesColon": "Taxes:", + "invoiceNotPrepared": "Invoice Not Prepared", + "makePayment": "Make Payment", + "typeMessage": "Type your message here", + "typeComment": "Enter Message Here", + "poNumberColon": "PO Number:", + "requirePONumber": "Please Enter a PO Number.", + "void": "Void", + "nameColon": "Name:", + "requireCustomer": "Please select one customer contact at least.", + "openInApp": "Open In App", + "layout": "Layout", + "resetLayout": "Reset Layout", + "resetPivots": "Reset Pivots", + "confirmResetLayout": "Are you sure you want to reset the layout?", + "selectColumns": "Select Columns", + "column": "Column", + "caption": "Caption", + "postNote": "Post Note", + "postedByColon": "Posted by:", + "emailedToColon": "Emailed to:", + "sendMessage": "Send Message", + "editContacts": "Edit Contacts", + "autoUpdateEnabled": "Auto Updates Enabled", + "autoUpdateDisabled": "Auto Updates Disabled", + "statusLinkIncluded": "Status Link Included", + "statusLinkExcluded": "Status Link Excluded" +} diff --git a/language/en/res.json b/language/en/res.json new file mode 100644 index 0000000..db2e887 --- /dev/null +++ b/language/en/res.json @@ -0,0 +1,211 @@ +{ + "ver": "#{$X-Res-Ver}", + "title": "Fleet Intelligence\u2122 Internal", + "logout": "Logout", + "workOrder": "Work Orders You Are Following", + "periodCol": "Period:", + "openedDateCol": "Opened Date:", + "closedDateCol": "Closed Date:", + "to": "to", + "threeMonths": "Within three months", + "thisYear": "This year", + "lastYear": "Last Year", + "all": "All", + "allOpened": "All Opened", + "thisMonth": "This Month", + "lastMonth": "Last Month", + "thisQuarter": "This Quarter", + "lastQuarter": "Last Quarter", + "yearToDate": "Year To Date", + "custom": "Custom", + "refresh": "Refresh", + "exportExcel": "Export to Excel", + "report": "Report", + "allItem": "( All )", + "ok": "OK", + "reset": "Reset", + "yes": "Yes", + "no": "No", + "unknownError": "An unknown error occurred, please contact the administrator.", + "searchHolder": "Search...", + "workOrderNumber": "Work Order Number", + "workOrderNumberWidth": 170, + "workOrderType": "Work Order Type", + "workOrderTypeWidth": 190, + "assignedTo": "Assigned To", + "assignedToWidth": 190, + "dealerName": "Dealer Name", + "dealerNameWidth": 270, + "status": "Status", + "statusWidth": 180, + "createDate": "Created Date", + "createDateWidth": 130, + "completeDate": "Completed Date", + "completeDateWidth": 130, + "completed": "Completed", + "totalCost": "Total Cost", + "totalCostWidth": 100, + "vin": "VIN", + "vinWidth": 150, + "make": "Make", + "makeColon": "Make: ", + "makeWidth": 150, + "model": "Model", + "modelColon": "Model: ", + "modelWidth": 150, + "type": "Type", + "typeWidth": 150, + "customerCode": "Customer Code", + "customerWidth": 180, + "userProfile": "User Profile", + "save": "Save", + "cancel": "Cancel", + "userName": "Display Name", + "mobilePhone": "Mobile Phone", + "timeZone": "Time Zone", + "language": "Language", + "companyName": "Company Name", + "companyNameWidth": 270, + "advisor": "Advisor", + "advisorWidth": 120, + "lastCommunicationText": "Last Communication Text", + "lastCommunicationTextWidth": 320, + "lastCommunicationBy": "Last Communication By", + "lastCommunicationByWidth": 190, + "lastCommunicationDate": "Last Communication Date", + "lastCommunicationDateWidth": 190, + "displayClosed": "Display Closed (90 days)", + "companyNameColon": "Company Name:", + "woNumberColon": "WO#:", + "savedSuccessfully": "Saved successfully.", + "viewWorkOrder": "Work Order - {0}", + "informationonAssetColon": "Information on Asset: ", + "workOrderColon": "Work Order: ", + "statusColon": "Status:", + "billingInformation": "Billing Information", + "estimateTotalAmountColon": "Estimate Total Amount: ", + "notesColon": "Notes: ", + "issuesIdentifiedinInspection": "Issues Identified in Inspection", + "identifiedIssues": "IDENTIFIED ISSUES", + "inspectionDetail": "INSPECTION DETAIL", + "noIdentifieds": "No Issues Identified in Inspection", + "severityLevelColon": "Severity Level: ", + "low": "Low", + "medium": "Medium", + "high": "High", + "noQuestions": "No Questions", + "workOrderAttachments": "Work Order Attachments", + "messages": "Customer Communication", + "internalComments": "Internal Comments", + "sentTo": "Sent To :", + "sent": "Sent", + "undelivered": "Undelivered", + "delivered": "Delivered", + "failed": "Failed", + "review": "Review", + "pending": "Pending", + "optOut": "Opt-Out", + "landline": "Landline", + "confirmSendMessage": "Message will be sent to:", + "confirmContinue": "Do you want to continue?", + "send": "Send", + "viewWorkOrderInspection": "View Work Order Inspection - {0}", + "viewFullInspection": "View Full Inspection", + "summary": "Summary", + "welcomeTitle": "Thank you for your business!", + "workOrderNumberColon": "Work Order Number:", + "openDateColon": "Open Date:", + "completedDateColon": "Completed Date:", + "approveRejectEstimate": "Approve/Reject Estimate", + "partsCostColon": "Parts Cost:", + "laborCostColon": "Labor Cost:", + "travelTimeCostColon": "Travel Time Cost:", + "otherCostColon": "Other Cost:", + "assetDetail": "Asset Detail", + "assetColon": "Asset:", + "hourMeterColon": "Hour Meter:", + "vinColon": "VIN:", + "typeColon": "Type:", + "odometerColon": "Odometer:", + "workOrderDetail": "Work Order Detail", + "viewApproveEstimate": "View/Approve Estimate", + "workOrderTypeColon": "Work Order Type:", + "customerReportedIssuesColon": "Issues:", + "workOrderTotalCostColon": "Work Order Total Cost:", + "estimateTotalCostColon": "Estimate Total Cost:", + "timeToCompleteColon": "Time To Complete(Hrs):", + "estimateDetail": "Estimate Detail", + "notesFromCustomerColon": "Notes from Customer:", + "approve": "Approve", + "reject": "Reject", + "approveEstimate": "Approve Estimate", + "rejectEstimate": "Reject Estimate", + "confirmApproveEstimate": "Are you sure you want to approve the estimate?", + "confirmRejectEstimate": "Are you sure you want to reject the estimate?", + "estimateStatusColon": "Estimate Status:", + "technicianNotesColon": "Technician Notes:", + "draft": "Draft", + "awaitingResponse": "Awaiting Response", + "closedByDealer": "Closed By Dealer", + "customerRejected": "Customer Rejected", + "customerApproved": "Customer Approved", + "estimateAttachments": "Estimate Attachments", + "photoDocuments": "Photos and Documentation", + "estimateNotPrepared": "Estimate Not Prepared", + "notAvailable": "N/A", + "signatureColon": "Signature:", + "signature": "Signature", + "requireSignature": "Please make a signature first.", + "clear": "Clear", + "approvedByColon": "Approved By:", + "phoneNumberColon": "Phone Number:", + "requireApprovedBy": "Please input the approver.", + "requirePhoneNumber": "Please input the phone number.", + "willWorkOurselves": "Will perform work ourselves", + "willWorkElsewhere": "Will have work done elsewhere", + "cost": "Cost", + "willNotWork": "Will not have work done", + "willWorkLater": "Will have work done at a later date", + "other": "Other", + "preferNotAnswer": "Prefer Not to Answer", + "reasonColon": "Reason:", + "termOfUse": "Terms of Use", + "requireReason": "Please select or input a reason.", + "submit": "Submit", + "survey": "Survey", + "thanksSurvey": "Your survey has been submitted successfully. Thank you for taking the survey.", + "surveyPrompt": "Please Rate the services that you received on work order {wonum} for your {make}/{model}", + "invoiceDetail": "Invoice Detail", + "awaitingPayment": "Awaiting Payment", + "paymentFailure": "Payment Failure", + "customerPaid": "Paid", + "invoiceStatusColon": "Invoice Status:", + "invoiceTotalCostColon": "Invoice Total Cost:", + "taxesColon": "Taxes:", + "invoiceNotPrepared": "Invoice Not Prepared", + "makePayment": "Make Payment", + "typeMessage": "Type your message here", + "typeComment": "Enter Message Here", + "poNumberColon": "PO Number:", + "requirePONumber": "Please Enter a PO Number.", + "void": "Void", + "nameColon": "Name:", + "requireCustomer": "Please select one customer contact at least.", + "openInApp": "Open In App", + "layout": "Layout", + "resetLayout": "Reset Layout", + "resetPivots": "Reset Pivots", + "confirmResetLayout": "Are you sure you want to reset the layout?", + "selectColumns": "Select Columns", + "column": "Column", + "caption": "Caption", + "postNote": "Post Note", + "postedByColon": "Posted by:", + "emailedToColon": "Emailed to:", + "sendMessage": "Send Message", + "editContacts": "Edit Contacts", + "autoUpdateEnabled": "Auto Updates Enabled", + "autoUpdateDisabled": "Auto Updates Disabled", + "statusLinkIncluded": "Status Link Included", + "statusLinkExcluded": "Status Link Excluded" +} diff --git a/language/fr/res.json b/language/fr/res.json new file mode 100644 index 0000000..fa76498 --- /dev/null +++ b/language/fr/res.json @@ -0,0 +1,211 @@ +{ + "ver": "#{$X-Res-Ver}", + "title": "Fleet Intelligence\u2122 Internal", + "logout": "Se déconnecter", + "workOrder": "Ordres de travail que vous suivez", + "periodCol": "Période:", + "openedDateCol": "Date d'ouverture:", + "closedDateCol": "Date de fermeture:", + "to": "à", + "threeMonths": "Dans les trois mois", + "thisYear": "Cette année", + "lastYear": "L'année dernière", + "all": "Tout", + "allOpened": "Tout ouvert", + "thisMonth": "Ce mois-ci", + "lastMonth": "Le mois dernier", + "thisQuarter": "Ce trimestre", + "lastQuarter": "Dernier quart", + "yearToDate": "Année à ce jour", + "custom": "Personnalisé", + "refresh": "Rafraîchir", + "exportExcel": "Exporter vers Excel", + "report": "Signaler", + "allItem": "( Tout )", + "ok": "d'accord", + "reset": "Réinitialiser", + "yes": "Oui", + "no": "Non", + "unknownError": "Une erreur inconnue s'est produite, veuillez contacter l'administrateur.", + "searchHolder": "Chercher...", + "workOrderNumber": "Numéro de l'ordre de travail", + "workOrderNumberWidth": 215, + "workOrderType": "Type d'ordre de travail", + "workOrderTypeWidth": 200, + "assignedTo": "Assigné à", + "assignedToWidth": 190, + "dealerName": "Le nom du revendeur", + "dealerNameWidth": 270, + "status": "Statut", + "statusWidth": 180, + "createDate": "Date de création", + "createDateWidth": 150, + "completeDate": "Date de fin", + "completeDateWidth": 130, + "completed": "Complété", + "totalCost": "Coût total", + "totalCostWidth": 100, + "vin": "VIN", + "vinWidth": 150, + "make": "Faire", + "makeColon": "Faire: ", + "makeWidth": 150, + "model": "Modèle", + "modelColon": "Modèle: ", + "modelWidth": 150, + "type": "Taper", + "typeWidth": 150, + "customerCode": "Code client", + "customerWidth": 180, + "userProfile": "Profil de l'utilisateur", + "save": "Sauvegarder", + "cancel": "Annuler", + "userName": "Afficher un nom", + "mobilePhone": "Téléphone portable", + "timeZone": "Fuseau horaire", + "language": "Langue", + "companyName": "Nom de la compagnie", + "companyNameWidth": 270, + "advisor": "Conseiller", + "advisorWidth": 120, + "lastCommunicationText": "Dernier texte de communication", + "lastCommunicationTextWidth": 320, + "lastCommunicationBy": "Dernière communication par", + "lastCommunicationByWidth": 215, + "lastCommunicationDate": "Date de la dernière communication", + "lastCommunicationDateWidth": 240, + "displayClosed": "Affichage fermé (90 jours)", + "companyNameColon": "Nom de la compagnie:", + "woNumberColon": "WO#:", + "savedSuccessfully": "Enregistré avec succès.", + "viewWorkOrder": "Ordre de travail - {0}", + "informationonAssetColon": "Informations sur l’actif : ", + "workOrderColon": "Ordre de travail: ", + "statusColon": "Statut:", + "billingInformation": "Informations de facturation", + "estimateTotalAmountColon": "Montant total estimatif: ", + "notesColon": "Notes: ", + "issuesIdentifiedinInspection": "Problèmes relevés lors de l’inspection", + "identifiedIssues": "PROBLÈMES IDENTIFIÉS", + "inspectionDetail": "DÉTAIL DE L'INSPECTION", + "noIdentifieds": "Aucun problème identifié lors de l'inspection", + "severityLevelColon": "Degré de gravité: ", + "low": "faible", + "medium": "dans", + "high": "haute", + "noQuestions": "Pas de problème.", + "workOrderAttachments": "Pièces jointes aux ordres de travail", + "messages": "Communication client", + "internalComments": "Commentaires internes", + "sentTo": "Envoyé à :", + "sent": "Expédié", + "undelivered": "Non livré", + "delivered": "Livré", + "failed": "Manqué", + "review": "La revue", + "pending": "En attente", + "optOut": "Se désengager", + "landline": "Téléphone fixe", + "confirmSendMessage": "Le message sera envoyé à :", + "confirmContinue": "Voulez-vous continuer ?", + "send": "Envoyer", + "viewWorkOrderInspection": "Afficher l’inspection des ordres de travail - {0}", + "viewFullInspection": "Voir l’inspection complète", + "summary": "Résumé", + "welcomeTitle": "Merci pour votre entreprise !", + "workOrderNumberColon": "Numéro de commande de travail :", + "openDateColon": "Date d'ouverture :", + "completedDateColon": "Date d'achèvement :", + "approveRejectEstimate": "Approuver/Rejeter l'estimation", + "partsCostColon": "Coût des pièces :", + "laborCostColon": "Coût du travail:", + "travelTimeCostColon": "Coût du temps de trajet :", + "otherCostColon": "Autre coût :", + "assetDetail": "Détail de l'actif", + "assetColon": "Actif:", + "hourMeterColon": "Compteur horaire :", + "vinColon": "VIN:", + "typeColon": "Taper:", + "odometerColon": "Odomètre:", + "workOrderDetail": "Détail de l'ordre de travail", + "viewApproveEstimate": "Voir/approuver l'estimation", + "workOrderTypeColon": "Type d'ordre de travail :", + "customerReportedIssuesColon": "Problèmes:", + "workOrderTotalCostColon": "Coût total de l'ordre de travail :", + "estimateTotalCostColon": "Estimer le coût total :", + "timeToCompleteColon": "Temps pour terminer (heures) :", + "estimateDetail": "Détail de l'estimation", + "notesFromCustomerColon": "Remarques du client :", + "approve": "Approuver", + "reject": "Rejeter", + "approveEstimate": "Approuver l'estimation", + "rejectEstimate": "Rejeter l'estimation", + "confirmApproveEstimate": "Êtes-vous sûr de vouloir approuver le devis ?", + "confirmRejectEstimate": "Êtes-vous sûr de vouloir rejeter le devis ?", + "estimateStatusColon": "État de l'estimation :", + "technicianNotesColon": "Notes du technicien :", + "draft": "Brouillon", + "awaitingResponse": "En attente de réponse", + "closedByDealer": "Fermé par le concessionnaire", + "customerRejected": "Client rejeté", + "customerApproved": "Approuvé par le client", + "estimateAttachments": "Estimer les pièces jointes", + "photoDocuments": "Photos et documentation", + "estimateNotPrepared": "Estimation non préparée", + "notAvailable": "N / A", + "signatureColon": "Signature:", + "signature": "Signature", + "requireSignature": "Veuillez d'abord faire une signature.", + "clear": "Dégager", + "approvedByColon": "Approuvé par:", + "phoneNumberColon": "Numéro de téléphone:", + "requireApprovedBy": "Veuillez saisir l'approbateur.", + "requirePhoneNumber": "Veuillez saisir le numéro de téléphone.", + "willWorkOurselves": "Effectuerons nous-mêmes le travail", + "willWorkElsewhere": "Aura le travail fait ailleurs", + "cost": "Coût", + "willNotWork": "N'aura pas de travail fait", + "willWorkLater": "Les travaux seront effectués à une date ultérieure", + "other": "Autre", + "preferNotAnswer": "Préfère ne pas répondre", + "reasonColon": "Raison:", + "termOfUse": "Conditions d'utilisation", + "requireReason": "Veuillez sélectionner ou saisir une raison.", + "submit": "Soumettre", + "survey": "Sondage", + "thanksSurvey": "Votre enquête a été soumise avec succès. Merci d'avoir répondu à l'enquête.", + "surveyPrompt": "Veuillez évaluer les services que vous avez reçus sur le bon de travail {wonum} pour votre {make}/{model}", + "invoiceDetail": "Détail de la facture", + "awaitingPayment": "En attente de paiement", + "paymentFailure": "Échec de paiement", + "customerPaid": "Payé", + "invoiceStatusColon": "État de la facture:", + "invoiceTotalCostColon": "Coût total de la facture:", + "taxesColon": "Impôts:", + "invoiceNotPrepared": "Facture non préparée", + "makePayment": "Effectuer le paiement", + "typeMessage": "Tapez votre message ici", + "typeComment": "Entrez le message ici", + "poNumberColon": "Numéro de bon de commande:", + "requirePONumber": "Veuillez saisir un numéro de bon de commande.", + "void": "Vide", + "nameColon": "Nom:", + "requireCustomer": "Veuillez sélectionner au moins un contact client.", + "openInApp": "Ouvrir dans l'application", + "layout": "Disposition", + "resetLayout": "Réinitialiser la mise en page", + "resetPivots": "Réinitialiser les pivots", + "confirmResetLayout": "Voulez-vous vraiment réinitialiser la mise en page?", + "selectColumns": "Sélectionnez les colonnes", + "column": "Colonne", + "caption": "Légende", + "postNote": "Note de poste", + "postedByColon": "Posté par:", + "emailedToColon": "Envoyé par e-mail à:", + "sendMessage": "Envoyer le message", + "editContacts": "Modifier les contacts", + "autoUpdateEnabled": "Mises à jour automatiques activées", + "autoUpdateDisabled": "Mises à jour automatiques désactivées", + "statusLinkIncluded": "Lien d'état inclus", + "statusLinkExcluded": "Lien de statut exclu" +} diff --git a/language/zh-cn/res.json b/language/zh-cn/res.json new file mode 100644 index 0000000..0baf210 --- /dev/null +++ b/language/zh-cn/res.json @@ -0,0 +1,211 @@ +{ + "ver": "#{$X-Res-Ver}", + "title": "Fleet Intelligence\u2122 Internal", + "logout": "注销", + "workOrder": "您关注的工单", + "periodCol": "时期:", + "openedDateCol": "开启日期:", + "closedDateCol": "关闭日期:", + "to": "至", + "threeMonths": "三个月内", + "thisYear": "今年", + "lastYear": "去年", + "all": "全部", + "allOpened": "所有已打开", + "thisMonth": "这个月", + "lastMonth": "上个月", + "thisQuarter": "这个季度", + "lastQuarter": "上个季度", + "yearToDate": "今年迄今为止", + "custom": "自定义", + "refresh": "刷新", + "exportExcel": "导出到 Excel", + "report": "报告", + "allItem": "( 全部 )", + "ok": "确定", + "reset": "重置", + "yes": "是", + "no": "否", + "unknownError": "发生未知错误,请联系管理员。", + "searchHolder": "搜索……", + "workOrderNumber": "工单号", + "workOrderNumberWidth": 110, + "workOrderType": "工单类型", + "workOrderTypeWidth": 190, + "assignedTo": "分配给", + "assignedToWidth": 190, + "dealerName": "设施点", + "dealerNameWidth": 270, + "status": "状态", + "statusWidth": 180, + "createDate": "创建日期", + "createDateWidth": 130, + "completeDate": "完成日期", + "completeDateWidth": 130, + "completed": "已完成", + "totalCost": "总开销", + "totalCostWidth": 100, + "vin": "VIN", + "vinWidth": 150, + "make": "制造商", + "makeColon": "制造商:", + "makeWidth": 150, + "model": "型号", + "modelColon": "型号:", + "modelWidth": 150, + "type": "类型", + "typeWidth": 150, + "customerCode": "客户代码", + "customerWidth": 180, + "userProfile": "用户资料", + "save": "保存", + "cancel": "取消", + "userName": "显示名称", + "mobilePhone": "手机", + "timeZone": "时区", + "language": "语言", + "companyName": "公司名称", + "companyNameWidth": 270, + "advisor": "顾问", + "advisorWidth": 120, + "lastCommunicationText": "最后通讯文本", + "lastCommunicationTextWidth": 320, + "lastCommunicationBy": "最后沟通者", + "lastCommunicationByWidth": 190, + "lastCommunicationDate": "最后通讯日期", + "lastCommunicationDateWidth": 130, + "displayClosed": "显示已关闭(90 天)", + "companyNameColon": "公司名称:", + "woNumberColon": "WO#:", + "savedSuccessfully": "保存成功。", + "viewWorkOrder": "工单 - {0}", + "informationonAssetColon": "机器信息:", + "workOrderColon": "工单:", + "statusColon": "状态:", + "billingInformation": "计费信息", + "estimateTotalAmountColon": "评估总金额:", + "notesColon": "备注:", + "issuesIdentifiedinInspection": "检查中发现的问题", + "identifiedIssues": "严重问题", + "inspectionDetail": "检查细节", + "noIdentifieds": "检查未发现问题", + "severityLevelColon": "严重性等级:", + "low": "低", + "medium": "中", + "high": "高", + "noQuestions": "无问题", + "workOrderAttachments": "工单附件", + "messages": "客户沟通", + "internalComments": "内部评论", + "sentTo": "已发给:", + "sent": "已发送", + "undelivered": "未送达", + "delivered": "已送达", + "failed": "失败", + "review": "审查", + "pending": "待定", + "optOut": "离开", + "landline": "座机", + "confirmSendMessage": "消息将发送至:", + "confirmContinue": "您要继续吗?", + "send": "发送", + "viewWorkOrderInspection": "工单检查 - {0}", + "viewFullInspection": "查看完整检查", + "summary": "摘要", + "welcomeTitle": "感谢您的业务!", + "workOrderNumberColon": "工单编号:", + "openDateColon": "开放日期:", + "completedDateColon": "完成日期:", + "approveRejectEstimate": "批准/拒绝评估", + "partsCostColon": "零件成本:", + "laborCostColon": "劳动力成本:", + "travelTimeCostColon": "旅行时间成本:", + "otherCostColon": "其他费用:", + "assetDetail": "资产详情", + "assetColon": "资产:", + "hourMeterColon": "计时器:", + "vinColon": "VIN:", + "typeColon": "类型:", + "odometerColon": "里程表:", + "workOrderDetail": "工单详情", + "viewApproveEstimate": "查看/批准评估", + "workOrderTypeColon": "工单类型:", + "customerReportedIssuesColon": "问题:", + "workOrderTotalCostColon": "工单总成本:", + "estimateTotalCostColon": "评估总成本:", + "timeToCompleteColon": "完成时间(小时):", + "estimateDetail": "评估详情", + "notesFromCustomerColon": "客户备注:", + "approve": "批准", + "reject": "拒绝", + "approveEstimate": "批准评估", + "rejectEstimate": "拒绝评估", + "confirmApproveEstimate": "您确定要批准估算吗?", + "confirmRejectEstimate": "您确定要拒绝估算吗?", + "estimateStatusColon": "评估状态:", + "technicianNotesColon": "技术员注意事项:", + "draft": "草稿", + "awaitingResponse": "等待答复", + "closedByDealer": "由经销商关闭", + "customerRejected": "顾客拒绝", + "customerApproved": "顾客同意", + "estimateAttachments": "评估附件", + "photoDocuments": "照片和文档", + "estimateNotPrepared": "评估未准备", + "notAvailable": "无", + "signatureColon": "签名:", + "signature": "签名", + "requireSignature": "请先签名。", + "clear": "清除", + "approvedByColon": "批准人:", + "phoneNumberColon": "电话号码:", + "requireApprovedBy": "请输入审批人。", + "requirePhoneNumber": "请输入电话号码。", + "willWorkOurselves": "将自己执行工作", + "willWorkElsewhere": "将在其他地方完成工作", + "cost": "成本", + "willNotWork": "将不会完成工作", + "willWorkLater": "将在以后完成工作", + "other": "其他", + "preferNotAnswer": "不想回答", + "reasonColon": "原因:", + "termOfUse": "使用条款", + "requireReason": "请选择或输入原因。", + "submit": "提交", + "survey": "调查问卷", + "thanksSurvey": "您的调查已成功提交。 感谢您参加调查。", + "surveyPrompt": "请为您的 {make}/{model} 的工单 {wonum} 获得的服务评分", + "invoiceDetail": "发票明细", + "awaitingPayment": "等待支付", + "paymentFailure": "支付失败", + "customerPaid": "已支付", + "invoiceStatusColon": "发票状态:", + "invoiceTotalCostColon": "发票总费用:", + "taxesColon": "税费:", + "invoiceNotPrepared": "未准备发票", + "makePayment": "付款", + "typeMessage": "在这里输入您的信息", + "typeComment": "在这里输入信息", + "poNumberColon": "订单号:", + "requirePONumber": "请输入采购订单号。", + "void": "无效", + "nameColon": "姓名:", + "requireCustomer": "请至少选择一位客户联系人。", + "openInApp": "在应用程序中打开", + "layout": "布局", + "resetLayout": "重置布局", + "resetPivots": "重置选择条件", + "confirmResetLayout": "您确定要重置布局吗?", + "selectColumns": "选择列", + "column": "列", + "caption": "标题", + "postNote": "发表评论", + "postedByColon": "发表者:", + "emailedToColon": "电邮至:", + "sendMessage": "发送信息", + "editContacts": "编辑联系人", + "autoUpdateEnabled": "自动更新已启用", + "autoUpdateDisabled": "自动更新已禁用", + "statusLinkIncluded": "已包含状态链接", + "statusLinkExcluded": "已排除状态链接" +} diff --git a/lib/app/communications/comments.js b/lib/app/communications/comments.js index 7b5fad7..ac8be82 100644 --- a/lib/app/communications/comments.js +++ b/lib/app/communications/comments.js @@ -5,26 +5,23 @@ import { createBox, appendMedia } from "./lib"; let r = lang; export default class CustomerRecordComment { - #container; - #option; - #enter; - #message; + _var = {}; constructor(opt) { - this.#option = opt ?? {}; + this._var.option = opt ?? {}; const getText = opt?.getText; if (typeof getText === 'function') { r = getText; } } - get text() { return this.#enter?.value } + get text() { return this._var.enter?.value } set text(s) { - const element = this.#enter; + const element = this._var.enter; if (element != null) { element.value = s - s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength); - this.#container.querySelector('.message-bar .prompt-count').innerText = s; + s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength); + this._var.container.querySelector('.message-bar .prompt-count').innerText = s; } } @@ -32,28 +29,28 @@ export default class CustomerRecordComment { * @param {boolean} flag */ set loading(flag) { - if (this.#container == null) { + if (this._var.container == null) { return; } - this.#enter.disabled = flag; - this.#container.querySelector('.button-send-message').disabled = flag; + this._var.enter.disabled = flag; + this._var.container.querySelector('.button-send-message').disabled = flag; } /** * @param {boolean} flag */ set readonly(flag) { - this.#option.readonly = flag; - if (this.#container == null) { + this._var.option.readonly = flag; + if (this._var.container == null) { return; } - this.#enter.disabled = flag === true; - this.#container.querySelector('.button-send-message').style.display = flag === true ? 'none' : ''; - this.#container.querySelector('.message-bar .prompt-count').style.display = flag === true ? 'none' : ''; + this._var.enter.disabled = flag === true; + this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : ''; + this._var.container.querySelector('.message-bar .prompt-count').style.display = flag === true ? 'none' : ''; } create() { - const readonly = this.#option.readonly; + const readonly = this._var.option.readonly; const container = createBox( createElement('div', null, createElement('div', div => { @@ -65,7 +62,7 @@ export default class CustomerRecordComment { createElement('button', button => { button.className = 'roundbtn button-close'; button.style.backgroundColor = 'transparent'; - if (this.#option.hasClose !== true) { + if (this._var.option.hasClose !== true) { button.style.display = 'none'; return; } @@ -73,8 +70,8 @@ export default class CustomerRecordComment { fill: '#000' })); button.addEventListener('click', () => { - if (typeof this.#option.onClose === 'function') { - this.#option.onClose(); + if (typeof this._var.option.onClose === 'function') { + this._var.option.onClose(); } }) }) @@ -83,16 +80,16 @@ export default class CustomerRecordComment { // enter box const enter = createElement('textarea', 'ui-text'); enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here'); - enter.maxLength = this.#option.maxLength ??= 3000; + enter.maxLength = this._var.option.maxLength ??= 3000; enter.addEventListener('input', () => { const val = this.text; - const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this.#option.maxLength); - this.#container.querySelector('.message-bar .prompt-count').innerText = s; + const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this._var.option.maxLength); + this._var.container.querySelector('.message-bar .prompt-count').innerText = s; }); if (readonly === true) { enter.disabled = true; } - this.#enter = enter; + this._var.enter = enter; container.appendChild( createElement('div', 'message-bar', enter, @@ -108,8 +105,8 @@ export default class CustomerRecordComment { // setTooltip(button, r('P_M3_SENDMESSAGE', 'Send Message')); setTooltip(button, r('P_CU_POSTNOTE', 'Post Note')); button.addEventListener('click', () => { - if (typeof this.#option.onAddComment === 'function') { - this.#option.onAddComment(this.text); + if (typeof this._var.option.onAddComment === 'function') { + this._var.option.onAddComment(this.text); } }) }) @@ -118,9 +115,9 @@ export default class CustomerRecordComment { ); const message = createElement('div', 'list-bar'); - this.#message = message; + this._var.message = message; container.appendChild(message); - return this.#container = container; + return this._var.container = container; } load(data) { @@ -129,7 +126,7 @@ export default class CustomerRecordComment { for (let comment of data) { const div = createElement('div', 'item-div'); // if (sendto !== '') { - // sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`; + // sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + `\n${sendto}`; // } div.appendChild(createElement('div', div => { div.className = 'item-poster'; @@ -137,8 +134,8 @@ export default class CustomerRecordComment { })); const content = createElement('div', 'item-content'); const mmsParts = createElement('div', div => div.style.display = 'none'); - content.appendChild(createElement('span', span => span.innerHTML = escapeEmoji(escapeHtml(comment.Comment)), mmsParts)); - if (comment.IsMMS && comment.MMSParts?.length > 0) { + content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Comment)), mmsParts)); + if (comment.MMSParts?.length > 0) { mmsParts.style.display = ''; for (let kv of comment.MMSParts) { appendMedia(mmsParts, kv.Key, kv.Value); @@ -155,8 +152,8 @@ export default class CustomerRecordComment { } children[0].style.marginTop = '0'; } - this.#message.replaceChildren(...children); - this.#message.scrollTop = this.#message.scrollHeight - // setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0); + this._var.message.replaceChildren(...children); + this._var.message.scrollTop = this._var.message.scrollHeight + // setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0); } } \ No newline at end of file diff --git a/lib/app/communications/contact.js b/lib/app/communications/contact.js index e5e7fe8..778aef3 100644 --- a/lib/app/communications/contact.js +++ b/lib/app/communications/contact.js @@ -4,11 +4,10 @@ import { isEmail, nullOrEmpty, r as lang } from "../../utility"; let r = lang; export class Contact { - #option; - #refs; + _var = {}; constructor(option = {}) { - this.#option = option; + this._var.option = option; const getText = option?.getText; if (typeof getText === 'function') { r = getText; @@ -18,7 +17,7 @@ export class Contact { async show(parent = document.body) { const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3; - const c = this.#option.contact; + const c = this._var.option.contact; const contactName = createElement('input', input => { input.type = 'text'; input.className = 'ui-input'; @@ -54,7 +53,7 @@ export class Contact { txt.style.height = '100px'; }); const buttons = []; - if (this.#option.company) { + if (this._var.option.company) { buttons.push({ text: c == null ? r('P_WO_ADDCONTACTRECORD', 'Add Contact Record') : r('P_WO_EDITCONTACTRECORD', 'Edit Contact Record'), // tabIndex: tabIndex + 7, @@ -64,8 +63,8 @@ export class Contact { return false; } item.SaveToCustomer = 1; - if (typeof this.#option.onSave === 'function') { - return this.#option.onSave.call(this, item, 'customerrecord'); + if (typeof this._var.option.onSave === 'function') { + return this._var.option.onSave.call(this, item, 'customerrecord'); } } }); @@ -81,8 +80,8 @@ export class Contact { } //item.Id = -1; item.SaveToCustomer = 0; - if (typeof this.#option.onSave === 'function') { - return this.#option.onSave.call(this, item, 'workorder'); + if (typeof this._var.option.onSave === 'function') { + return this._var.option.onSave.call(this, item, 'workorder'); } } }, @@ -92,7 +91,7 @@ export class Contact { } ); const popup = new Popup({ - onMasking: this.#option.onMasking, + onMasking: this._var.option.onMasking, title: c == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact'), content: createElement('div', wrapper => { wrapper.className = 'setting-wrapper'; @@ -135,7 +134,7 @@ export class Contact { } else { preferences.select('0'); } - this.#refs = { + this._var.refs = { contactName, preferences, contactEmail, @@ -149,35 +148,35 @@ export class Contact { } prepare() { - const name = this.#refs.contactName.value; - const pref = this.#refs.preferences.selected.value; - const email = this.#refs.contactEmail.value; - const phone = this.#refs.contactMobile.value; - const opt = this.#refs.checkOpt.querySelector('input').checked; - const notes = this.#refs.contactNotes.value; - const title = this.#option.contact == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact'); + const name = this._var.refs.contactName.value; + const pref = this._var.refs.preferences.selected.value; + const email = this._var.refs.contactEmail.value; + const phone = this._var.refs.contactMobile.value; + const opt = this._var.refs.checkOpt.querySelector('input').checked; + const notes = this._var.refs.contactNotes.value; + const title = this._var.option.contact == null ? r('P_CR_ADDCONTACT', 'Add Contact') : r('P_CR_EDITCONTACT', 'Edit Contact'); if (nullOrEmpty(name)) { showAlert(title, r('P_CR_CONTACTNAMECANNOTBEEMPTY', 'Contact Name cannot be empty.'), 'warn') - .then(() => this.#refs.contactName.focus()); + .then(() => this._var.refs.contactName.focus()); return null; } if ((pref == 0 || pref == 2) && nullOrEmpty(phone)) { showAlert(title, r('P_CR_MOBILECANNOTBEEMPTY', 'Mobile cannot be empty.'), 'warn') - .then(() => this.#refs.contactMobile.focus()); + .then(() => this._var.refs.contactMobile.focus()); return null; } if (pref == 1 && nullOrEmpty(email)) { showAlert(title, r('P_CU_EMAILCANNOTBEEMPTY', 'Email cannot be empty.'), 'warn') - .then(() => this.#refs.contactEmail.focus()); + .then(() => this._var.refs.contactEmail.focus()); return null; } if (!nullOrEmpty(email) && !isEmail(email)) { showAlert(title, r('P_CR_EMAILISNOTAVALIDEMAILADDRESS', 'The email address is invalid.'), 'warn') - .then(() => this.#refs.contactEmail.focus()); + .then(() => this._var.refs.contactEmail.focus()); return null; } - let contact = this.#option.contact; + let contact = this._var.option.contact; if (contact == null) { contact = {}; } else if (contact.OptOut !== opt) { @@ -198,11 +197,10 @@ export class Contact { } export class CustomerRecordContact { - #option; - #grid; + _var = {}; constructor(option = {}) { - this.#option = option; + this._var.option = option; const getText = option?.getText; if (typeof getText === 'function') { r = getText; @@ -214,7 +212,7 @@ export class CustomerRecordContact { const gridContainer = createElement('div', 'selcontact-grid'); const popup = new Popup({ - onMasking: this.#option.onMasking, + onMasking: this._var.option.onMasking, title, content: createElement('div', 'selcontact-wrapper', gridContainer @@ -224,8 +222,8 @@ export class CustomerRecordContact { text: r('P_WO_OK', 'OK'), key: 'ok', trigger: () => { - if (typeof this.#option.onOk === 'function') { - return this.#option.onOk.call(this, this.#grid.source.filter(f => f.selected)); + if (typeof this._var.option.onOk === 'function') { + return this._var.option.onOk.call(this, this._var.grid.source.filter(f => f.selected)); } } }, @@ -250,16 +248,16 @@ export class CustomerRecordContact { { key: 'Notes', caption: r("P_CR_NOTES", "Notes"), width: 120 } ]; grid.init(); - grid.source = this.#option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) }); - this.#grid = grid; + grid.source = this._var.option.contacts.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) }); + this._var.grid = grid; return result; } set source(contacts) { - this.#option.contacts = contacts; - const grid = this.#grid; + this._var.option.contacts = contacts; + const grid = this._var.grid; if (grid != null) { - this.#grid.source = contacts; + this._var.grid.source = contacts; } } } \ No newline at end of file diff --git a/lib/app/communications/customer.js b/lib/app/communications/customer.js index be6ece0..835e8a4 100644 --- a/lib/app/communications/customer.js +++ b/lib/app/communications/customer.js @@ -1,6 +1,6 @@ import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup } from "../../ui"; import { r as lang, nullOrEmpty, formatUrl, escapeEmoji, isEmail, isPhone } from "../../utility"; -import { createBox, appendMedia, fileSupported, insertFile } from "./lib"; +import { createBox, appendMedia, fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName } from "./lib"; import { Contact, CustomerRecordContact } from "./contact"; import Follower from "./follower"; @@ -31,31 +31,22 @@ class NoteCol extends GridColumn { let r = lang; export default class CustomerCommunication { - #container; - #option; - #contacts; - #followers; - #buttonFollower; - #enter; - #fileControl; - #file; - #message; - #data = {}; - #gridContact; - #gridWo; + _var = { + data: {} + }; constructor(opt) { - this.#option = opt ?? {}; + this._var.option = opt ?? {}; const getText = opt?.getText; if (typeof getText === 'function') { r = getText; } } - get #autoUpdates() { return this.#container.querySelector('.check-auto-update>input') } - get autoUpdatesEnabled() { return this.#autoUpdates?.disabled !== true } + get _autoUpdates() { return this._var.container.querySelector('.check-auto-update>input') } + get autoUpdatesEnabled() { return this._autoUpdates?.disabled !== true } set autoUpdatesEnabled(flag) { - const element = this.#autoUpdates; + const element = this._autoUpdates; if (element == null) { return; } @@ -67,9 +58,9 @@ export default class CustomerCommunication { element.parentElement?.classList?.remove('disabled'); } } - get autoUpdates() { return this.#autoUpdates?.checked } + get autoUpdates() { return this._autoUpdates?.checked } set autoUpdates(flag) { - const element = this.#autoUpdates; + const element = this._autoUpdates; if (element == null) { return; } @@ -77,10 +68,10 @@ export default class CustomerCommunication { element.dispatchEvent(new Event('change')); } - get #statusLink() { return this.#container.querySelector('.check-status-link>input') } - get statusLinkEnabled() { return this.#statusLink?.disabled !== true } + get _statusLink() { return this._var.container.querySelector('.check-status-link>input') } + get statusLinkEnabled() { return this._statusLink?.disabled !== true } set statusLinkEnabled(flag) { - const element = this.#statusLink; + const element = this._statusLink; if (element == null) { return; } @@ -92,9 +83,9 @@ export default class CustomerCommunication { element.parentElement?.classList?.remove('disabled'); } } - get statusLink() { return this.#statusLink?.checked } + get statusLink() { return this._statusLink?.checked } set statusLink(flag) { - const element = this.#statusLink; + const element = this._statusLink; if (element == null) { return; } @@ -106,33 +97,33 @@ export default class CustomerCommunication { * @param {boolean} flag */ set loading(flag) { - if (this.#container == null) { + if (this._var.container == null) { return; } - this.#enter.disabled = flag; - this.#container.querySelector('.customer-name>.ui-input').disabled = flag; - this.#container.querySelector('.button-send-message').disabled = flag; - this.#container.querySelector('.button-edit-contacts').disabled = flag; - this.#container.querySelector('.button-edit-followers').disabled = flag; + this._var.enter.disabled = flag; + this._var.container.querySelector('.customer-name>.ui-input').disabled = flag; + this._var.container.querySelector('.button-send-message').disabled = flag; + this._var.container.querySelector('.button-edit-contacts').disabled = flag; + this._var.container.querySelector('.button-edit-followers').disabled = flag; } - get text() { return this.#enter?.value } + get text() { return this._var.enter?.value } set text(s) { - const element = this.#enter; + const element = this._var.enter; if (element != null) { element.value = s - s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength); - this.#container.querySelector('.message-bar .prompt-count').innerText = s; + s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength); + this._var.container.querySelector('.message-bar .prompt-count').innerText = s; } } - get file() { return this.#file || this.#fileControl?.files?.[0] } + get file() { return this._var.file || this._var.fileControl?.files?.[0] } set file(f) { - this.#fileControl?.remove(); - const label = this.#container.querySelector('.file-selector>.selector-name'); + this._var.fileControl?.remove(); + const label = this._var.container.querySelector('.file-selector>.selector-name'); if (f == null) { - this.#fileControl = null; - this.#file = null; + this._var.fileControl = null; + this._var.file = null; if (label != null) { label.style.display = 'none'; label.innerText = ''; @@ -140,15 +131,15 @@ export default class CustomerCommunication { } } else { if (f instanceof HTMLInputElement) { - this.#fileControl = f; - this.#file = f.files[0]; - const link = this.#container.querySelector('.file-selector>.selector-link'); + this._var.fileControl = f; + this._var.file = f.files[0]; + const link = this._var.container.querySelector('.file-selector>.selector-link'); if (link != null) { link.appendChild(f); } } else { - this.#fileControl = null; - this.#file = f; + this._var.fileControl = null; + this._var.file = f; } if (label != null) { label.style.display = ''; @@ -158,9 +149,9 @@ export default class CustomerCommunication { } } - get customerName() { return this.#container.querySelector('.customer-name>.ui-input')?.value } + get customerName() { return this._var.container.querySelector('.customer-name>.ui-input')?.value } set customerName(name) { - const element = this.#container.querySelector('.customer-name>.ui-input'); + const element = this._var.container.querySelector('.customer-name>.ui-input'); if (element == null) { return; } @@ -168,7 +159,7 @@ export default class CustomerCommunication { } get contacts() { - return [...this.#contacts.children].filter(el => { + return [...this._var.contacts.children].filter(el => { return el.querySelector('span').dataset.notsend == "false"; }).map(el => { const span = el.querySelector('span'); @@ -176,11 +167,20 @@ export default class CustomerCommunication { }); } set contacts(contacts) { - this.#contacts.replaceChildren(); + this.setData('contacts', contacts); + this._var.contacts.replaceChildren(); if (contacts?.length > 0) { var cs = contacts.sort(function (a, b) { - if (a.Name == b.Name) return 0; return a.Name > b.Name ? 1 : -1; + if (a.Name == b.Name) { + return 0; + } + return a.Name > b.Name ? 1 : -1; }); + const messages = this._var.data.messages; + if (this._var.contactsUpdated !== true && messages?.length > 0) { + updateCustomerName(messages, contacts); + this._var.contactsUpdated = true; + } for (let c of cs) { //if (c.OptOut || c.OptOut_BC || c.selected === false) { // continue; @@ -195,19 +195,28 @@ export default class CustomerCommunication { const to = pref === '1' ? email : mp; let icon; let method; + let tipstr; if (c.OptOut || c.OptOut_BC || c.selected === false) { icon = 'times'; - method = r('P_CU_OPTEDOUT_COLON', 'Opted Out:'); + tipstr = r('P_CU_OPTEDOUT_PROMPT', 'User has opted out of messages'); } else { switch (pref) { case '0': - icon = 'comment-lines'; + if (c.MobilePhoneStatus !== 0) { + icon = 'times'; + if (c.MobilePhoneStatus === 412) { + // landline + tipstr = r('P_CU_LANDLINE', 'Landline'); + } + } else { + icon = 'comment-lines'; + } method = r('P_CU_TEXTSTO_COLON', 'Texts to:'); break; case '2': - icon = 'mobile'; - method = r('P_CU_CALLSTO_COLON', 'Calls to:'); + icon = 'phone'; + tipstr = r('P_CU_NOMESSAGE', 'No Messages Sent'); break; default: icon = 'envelope'; @@ -225,14 +234,14 @@ export default class CustomerCommunication { createIcon('fa-light', icon, { 'fill': (c.OptOut || c.OptOut_BC || c.selected === false) ? 'red' : '' }), span ); - this.#contacts.appendChild(item); - let tip = `${method} ${to}`; + this._var.contacts.appendChild(item); + let tip = tipstr || `${method} ${to}`; if (span.scrollWidth > span.offsetWidth) { tip = r('P_WO_NAME_COLON', 'Name:') + ` ${c.Name}\n${tip}`; } setTooltip(span, tip); } - this.#message.scrollTop = this.#message.scrollHeight + this._var.message.scrollTop = this._var.message.scrollHeight } } @@ -240,11 +249,11 @@ export default class CustomerCommunication { * @param {boolean} flag */ set readonly(flag) { - this.#option.readonly = flag; - if (this.#container == null) { + this._var.option.readonly = flag; + if (this._var.container == null) { return; } - const link = this.#container.querySelector('.check-status-link'); + const link = this._var.container.querySelector('.check-status-link'); if (flag === true) { link.classList.add('disabled'); } else { @@ -252,31 +261,31 @@ export default class CustomerCommunication { } link.querySelector('input').disabled = flag; const display = flag === true ? 'none' : ''; - this.#container.querySelector('.button-edit-contacts').style.display = display; - this.#container.querySelector('.button-edit-followers').style.display = display; - // this.#enter.disabled = flag === true; - this.#container.querySelector('.message-bar').style.display = display; - // this.#container.querySelector('.button-send-message').style.display = display; + this._var.container.querySelector('.button-edit-contacts').style.display = display; + this._var.container.querySelector('.button-edit-followers').style.display = display; + // this._var.enter.disabled = flag === true; + this._var.container.querySelector('.message-bar').style.display = display; + // this._var.container.querySelector('.button-send-message').style.display = display; } /** * @param {boolean} flag */ set recordReadonly(flag) { - this.#option.recordReadonly = flag; - if (this.#container == null) { + this._var.option.recordReadonly = flag; + if (this._var.container == null) { return; } - this.#container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : ''; - this.#container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : ''; + this._var.container.querySelector('.button-edit-contacts').style.display = flag === true ? 'none' : ''; + this._var.container.querySelector('.button-edit-followers').style.display = flag === true ? 'none' : ''; } /** * @param {String} name */ set companyName(name) { - this.#option.companyName = name; - const div = this.#container.querySelector('.title-company'); + this._var.option.companyName = name; + const div = this._var.container.querySelector('.title-company'); if (nullOrEmpty(name)) { div.style.display = 'none'; } else { @@ -288,9 +297,9 @@ export default class CustomerCommunication { * @param {String} code */ set companyCode(code) { - const option = this.#option; + const option = this._var.option; option.companyCode = code; - const div = this.#container.querySelector('.title-company'); + const div = this._var.container.querySelector('.title-company'); if (nullOrEmpty(option.companyName)) { div.style.display = 'none'; } else { @@ -302,18 +311,18 @@ export default class CustomerCommunication { } get followers() { - return [...this.#followers.children].map(el => { + return [...this._var.followers.children].map(el => { const span = el.querySelector('span'); return { 'Email': span.dataset.email, 'MobilePhone': span.dataset.mp, 'Value': span.dataset.name }; }); } set followers(followers) { - this.#data.followers = followers; - this.#followers.replaceChildren(); + this._var.data.followers = followers; + this._var.followers.replaceChildren(); if (followers?.length > 0) { - this.#container.querySelector('.follower-bar').style.display = ''; - setTooltip(this.#buttonFollower, r('P_CU_EDITFOLLOWERS', 'Edit Followers')); - this.#container.querySelector('.follower-bar>.bar-list').appendChild(this.#buttonFollower); + this._var.container.querySelector('.follower-bar').style.display = ''; + setTooltip(this._var.buttonFollower, r('P_CU_EDITFOLLOWERS', 'Edit Followers')); + this._var.container.querySelector('.follower-bar>.bar-list').appendChild(this._var.buttonFollower); for (let f of followers) { if (f.OptOut) { continue; @@ -326,7 +335,7 @@ export default class CustomerCommunication { tips.push(r('P_CU_EMAILSTO_COLON', 'Emails to:') + ` ${email}`); } if (f.SendText) { - tips.push(r('P_CU_TEXTSTO_COLON', 'Texts to:' + ` ${mpDisplay}`)); + tips.push(r('P_CU_TEXTSTO_COLON', 'Texts to:') + ` ${mpDisplay}`); } let icon; if (f.SendText && f.SendEmail) { @@ -348,26 +357,26 @@ export default class CustomerCommunication { createIcon('fa-light', icon), span ); - this.#followers.appendChild(item); + this._var.followers.appendChild(item); if (span.scrollWidth > span.offsetWidth) { tips.splice(0, 0, r('P_WO_NAME_COLON', 'Name:') + ` ${c.Name}`); } setTooltip(span, tips.join('\n')); } } else { - this.#container.querySelector('.follower-bar').style.display = 'none'; - setTooltip(this.#buttonFollower, r('P_CR_ADDFOLLOWERS', 'Add Followers')); - this.#container.querySelector('.button-edit-contacts').insertAdjacentElement('beforebegin', this.#buttonFollower) + this._var.container.querySelector('.follower-bar').style.display = 'none'; + setTooltip(this._var.buttonFollower, r('P_CR_ADDFOLLOWERS', 'Add Followers')); + this._var.container.querySelector('.button-edit-contacts').insertAdjacentElement('beforebegin', this._var.buttonFollower) } - this.#message.scrollTop = this.#message.scrollHeight + this._var.message.scrollTop = this._var.message.scrollHeight } setData(key, data) { - this.#data[key] = data; + this._var.data[key] = data; } create() { - const option = this.#option; + const option = this._var.option; const readonly = option.readonly; // functions const checkAutoUpdate = createCheckbox({ @@ -428,9 +437,9 @@ export default class CustomerCommunication { ] ); // contacts - this.#contacts = this.#createContacts(container, option); + this._var.contacts = this._createContacts(container, option); // followers - this.#followers = this.#createFollowers(container, option); + this._var.followers = this._createFollowers(container, option); // enter box const enter = createElement('textarea', 'ui-text'); enter.placeholder = r('P_CU_ENTERMESSAGEHERE', 'Enter Message Here'); @@ -442,7 +451,7 @@ export default class CustomerCommunication { enter.addEventListener('input', () => { const val = this.text; const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(option.maxLength); - this.#container.querySelector('.message-bar .prompt-count').innerText = s; + this._var.container.querySelector('.message-bar .prompt-count').innerText = s; }); enter.addEventListener('paste', e => { if (option.customerNameVisible === true) { @@ -466,7 +475,7 @@ export default class CustomerCommunication { // this.file = insertFile(container, file, r); // } }); - this.#enter = enter; + this._var.enter = enter; container.appendChild( createElement('div', div => { div.className = 'message-bar'; @@ -498,52 +507,49 @@ export default class CustomerCommunication { }, enter, createElement('div', div => div.style.textAlign = 'right', - createElement('div', div => { - div.className = 'customer-name'; - if (option.customerNameVisible !== true) { - div.style.display = 'none'; - } - }, - createElement('span', span => span.innerText = r('P_WO_NAME_COLON', 'Name:')), - createElement('input', input => { - input.type = 'text'; - input.className = 'ui-input'; - }) - ), - createElement('div', selector => { - selector.className = 'file-selector'; - if (option.customerNameVisible === true) { - selector.style.display = 'none'; - } - }, + createElement('div', 'customer-left', createElement('div', div => { - div.className = 'selector-link'; - div.addEventListener('click', () => { - this.#fileControl?.remove(); - const file = createElement('input', input => { - input.type = 'file'; - input.accept = fileSupported.join(','); - input.addEventListener('change', () => { - const file = insertFile(container, input.files?.[0], r); - if (file != null) { - this.file = file; - } - }); - }); - div.appendChild(this.#fileControl = file); - file.dispatchEvent(new MouseEvent('click')); - }); + div.className = 'customer-name'; + if (option.customerNameVisible !== true) { + div.style.display = 'none'; + } }, - createIcon('fa-regular', 'link') + createElement('span', span => span.innerText = r('P_WO_NAME_COLON', 'Name:')), + createElement('input', input => { + input.type = 'text'; + input.className = 'ui-input'; + }) ), - createElement('span', span => { - span.className = 'selector-name'; - span.style.display = 'none'; - }), - createElement('layer', layer => { - layer.appendChild(createIcon('fa-regular', 'times')); - layer.addEventListener('click', () => this.file = null); - }) + createElement('div', 'file-selector', + createElement('div', div => { + div.className = 'selector-link'; + div.addEventListener('click', () => { + this._var.fileControl?.remove(); + const file = createElement('input', input => { + input.type = 'file'; + input.accept = fileSupported.join(','); + input.addEventListener('change', () => { + const file = insertFile(container, input.files?.[0], r); + if (file != null) { + this.file = file; + } + }); + }); + div.appendChild(this._var.fileControl = file); + file.dispatchEvent(new MouseEvent('click')); + }); + }, + createIcon('fa-regular', 'link') + ), + createElement('span', span => { + span.className = 'selector-name'; + span.style.display = 'none'; + }), + createElement('layer', layer => { + layer.appendChild(createIcon('fa-regular', 'times')); + layer.addEventListener('click', () => this.file = null); + }) + ) ), createElement('div', 'prompt-count'), createElement('button', button => { @@ -575,12 +581,12 @@ export default class CustomerCommunication { ); const message = createElement('div', 'list-bar'); - this.#message = message; + this._var.message = message; container.appendChild(message); - return this.#container = container; + return this._var.container = container; } - #createContacts(container, option) { + _createContacts(container, option) { const readonly = option.readonly; const recordReadonly = option.recordReadonly; const contacts = createElement('div', 'bar-list-container'); @@ -616,6 +622,7 @@ export default class CustomerCommunication { createElement('div', div => div.innerText = r('P_CU_EDITCONTACTS', 'Edit Contacts')), createElement('div', div => { div.className = 'title-company'; + div.style.maxWidth = '540px'; if (nullOrEmpty(option.companyName)) { div.style.display = 'none'; } else { @@ -652,8 +659,8 @@ export default class CustomerCommunication { }); const result = option.onSelectCRContacts(list); } - const r = this.#data.contacts; - this.#gridContact.source = r.filter(c => c.Id >= 0).map(c => { + const r = this._var.data.contacts; + this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => { if (c.OptOut || c.OptOut_BC) { return c; } @@ -662,7 +669,7 @@ export default class CustomerCommunication { } return c; }); - this.#gridWo.source = r.filter(c => c.Id < 0).map(c => { + this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => { if (c.OptOut || c.OptOut_BC) { return c; } @@ -681,7 +688,7 @@ export default class CustomerCommunication { if (typeof result?.then === 'function') { return result.then(r => { r.map(c => { - for (let cc of this.#data.contacts) { + for (let cc of this._var.data.contacts) { if (c.Id === cc.Id) { c.selected = true; break; @@ -692,7 +699,7 @@ export default class CustomerCommunication { } return c; }); - this.#data.contacts.filter(c => c.Id >= 0).map(c => { + this._var.data.contacts.filter(c => c.Id >= 0).map(c => { if (c.OptOut || c.OptOut_BC) { return c; } @@ -729,7 +736,7 @@ export default class CustomerCommunication { // onMasking: option.onMasking, company: !nullOrEmpty(option.companyName), onSave: item => { - const exists = this.#gridContact.source.some(s => s.Name === item.Name && s.MobilePhone === item.MobilePhone); + const exists = this._var.gridContact.source.some(s => s.Name === item.Name && s.MobilePhone === item.MobilePhone); if (exists) { showAlert(r('P_CR_ADDCONTACT', 'Add Contact'), r('P_WO_CONTACTNAMEANDMOBILEUNIQUECOMBINATION', 'Contact name and contact mobile must be a unique combination.'), 'warn'); return false; @@ -738,7 +745,7 @@ export default class CustomerCommunication { const result = option.onSave(item, true); if (typeof result?.then === 'function') { return result.then(r => { - this.#gridContact.source = r.filter(c => c.Id >= 0).map(c => { + this._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => { if (c.OptOut || c.OptOut_BC) { return c; } @@ -747,7 +754,7 @@ export default class CustomerCommunication { } return c; }); - this.#gridWo.source = r.filter(c => c.Id < 0).map(c => { + this._var.gridWo.source = r.filter(c => c.Id < 0).map(c => { if (c.OptOut || c.OptOut_BC) { return c; } @@ -807,7 +814,7 @@ export default class CustomerCommunication { enabled: item => !item.OptOut && !item.OptOut_BC, onchanged: function () { if (typeof option.onChanged === 'function') { - option.onChanged([...This.#gridContact.source, ...This.#gridWo.source]); + option.onChanged([...This._var.gridContact.source, ...This._var.gridWo.source]); } }, tooltip: item => item.selected ? r('P_CU_OPTEDIN', 'Opted In') : r('P_CU_OPTEDOUT', 'Opted Out') @@ -850,8 +857,8 @@ export default class CustomerCommunication { company: !nullOrEmpty(option.companyName), onSave: (item, _op) => { const exists = - This.#gridContact.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone) || - This.#gridWo.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone); + This._var.gridContact.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone) || + This._var.gridWo.source.some(s => s !== this && s.Name === item.Name && s.MobilePhone === item.MobilePhone); if (exists) { showAlert(r('P_CR_EDITCONTACT', 'Edit Contact'), r('P_WO_CONTACTNAMEANDMOBILEUNIQUECOMBINATION', 'Contact name and contact mobile must be a unique combination.'), 'warn'); return false; @@ -860,7 +867,7 @@ export default class CustomerCommunication { const result = option.onSave(item); if (typeof result?.then === 'function') { return result.then(r => { - This.#gridContact.source = r.filter(c => c.Id >= 0).map(c => { + This._var.gridContact.source = r.filter(c => c.Id >= 0).map(c => { if (c.OptOut || c.OptOut_BC) { return c; } @@ -869,7 +876,7 @@ export default class CustomerCommunication { } return c; }); - This.#gridWo.source = r.filter(c => c.Id < 0).map(c => { + This._var.gridWo.source = r.filter(c => c.Id < 0).map(c => { if (c.OptOut || c.OptOut_BC) { return c; } @@ -957,7 +964,7 @@ export default class CustomerCommunication { } ]; grid.init(pop.container.querySelector('.contacts-record')); - const customerRecords = this.#data.contacts.filter(c => c.Id >= 0).map(c => { + const customerRecords = this._var.data.contacts.filter(c => c.Id >= 0).map(c => { if (c.OptOut || c.OptOut_BC) { return c; } @@ -969,11 +976,11 @@ export default class CustomerCommunication { grid.extraRows = customerRecords.filter(c => !nullOrEmpty(c.Notes)).length; grid.source = customerRecords; grid.selectedRowChanged = index => { - if (index >= 0 && this.#gridWo.selectedIndexes?.length > 0) { - this.#gridWo.selectedIndexes = []; + if (index >= 0 && this._var.gridWo.selectedIndexes?.length > 0) { + this._var.gridWo.selectedIndexes = []; } }; - this.#gridContact = grid; + this._var.gridContact = grid; // contacts from work order only const gridWo = new Grid(); @@ -1016,7 +1023,7 @@ export default class CustomerCommunication { } ]; gridWo.init(pop.container.querySelector('.contacts-wo')); - const workOrderOnly = this.#data.contacts.filter(c => c.Id < 0).map(c => { + const workOrderOnly = this._var.data.contacts.filter(c => c.Id < 0).map(c => { if (c.OptOut || c.OptOut_BC) { return c; } @@ -1028,11 +1035,11 @@ export default class CustomerCommunication { gridWo.extraRows = workOrderOnly.filter(c => !nullOrEmpty(c.Notes)).length; gridWo.source = workOrderOnly; gridWo.selectedRowChanged = index => { - if (index >= 0 && this.#gridContact.selectedIndexes?.length > 0) { - this.#gridContact.selectedIndexes = []; + if (index >= 0 && this._var.gridContact.selectedIndexes?.length > 0) { + this._var.gridContact.selectedIndexes = []; } }; - this.#gridWo = gridWo; + this._var.gridWo = gridWo; }); }); }) @@ -1072,7 +1079,7 @@ export default class CustomerCommunication { return contacts; } - #createFollowers(container, option) { + _createFollowers(container, option) { const readonly = option.readonly; const recordReadonly = option.recordReadonly; const followers = createElement('div', 'bar-list-container'); @@ -1086,7 +1093,7 @@ export default class CustomerCommunication { setTooltip(button, r('P_CU_EDITFOLLOWERS', 'Edit Followers')); button.addEventListener('click', () => { if (typeof option.onInitFollower === 'function') { - option.onInitFollower(this.#data.followers).then(data => { + option.onInitFollower(this._var.data.followers).then(data => { if (typeof data === 'string') { showAlert(r('P_CUSTOMERRECORD', 'Customer Record'), data, 'warn'); return; @@ -1108,13 +1115,13 @@ export default class CustomerCommunication { } } }); - var title = this.#data.followers?.length > 0 ? r('P_CU_EDITFOLLOWERS', 'Edit Followers') : r('P_CR_ADDFOLLOWERS', 'Add Followers'); + var title = this._var.data.followers?.length > 0 ? r('P_CU_EDITFOLLOWERS', 'Edit Followers') : r('P_CR_ADDFOLLOWERS', 'Add Followers'); add.show(title, container); }); } }); }); - this.#buttonFollower = buttonEditFollower; + this._var.buttonFollower = buttonEditFollower; container.append( createElement('div', div => { div.className = 'contact-bar follower-bar'; @@ -1141,8 +1148,16 @@ export default class CustomerCommunication { load(data, contacts, followers) { const children = []; if (data?.length > 0) { - contacts ??= this.#data.contacts; - followers ??= this.#data.allfollowers; + contacts ??= this._var.data.contacts; + followers ??= this._var.data.allfollowers; + this.setData('messages', data); + if (this._var.contactsUpdated !== true) { + const contacts = this._var.data.contacts; + if (contacts?.length > 0) { + updateCustomerName(data, contacts); + this._var.contactsUpdated = true; + } + } for (let comm of data) { const div = createElement('div', 'item-div'); let name; @@ -1153,32 +1168,7 @@ export default class CustomerCommunication { name = c?.Name; } name ??= comm.IsReply && String(comm.FormatSender) !== '' ? comm.FormatSender : comm.Sender; - let sendto = ''; - if (!comm.IsReply && comm.OriPhoneNumbers?.length > 0) { - for (let oriph of comm.OriPhoneNumbers) { - let cname; - const email = isEmail(oriph); - if (contacts?.length > 0) { - let c = email ? - contacts.find(c => c.Email === oriph) : - contacts.find(c => c.MobilePhone === oriph); - if (c != null) { - cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`; - } else if (followers?.length > 0) { - c = email ? - followers.find(f => f.Email === oriph) : - followers.find(f => f.MobilePhone === oriph); - if (c != null) { - cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`; - } - } - } - sendto += (cname ?? oriph) + '\n'; - } - } - if (sendto !== '') { - sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`; - } + const sendto = getMessageSendTo(comm, contacts, followers, r) div.appendChild(createElement('div', div => { div.className = 'item-poster'; div.innerText = name; @@ -1190,13 +1180,13 @@ export default class CustomerCommunication { const mmsParts = createElement('div', div => div.style.display = 'none'); content.appendChild(createElement('span', span => { if (/https?:\/\//i.test(comm.Message)) { - span.innerHTML = escapeEmoji(formatUrl(comm.Message)); + span.innerHTML = formatUrl(escapeEmoji(comm.Message)); } else { span.innerText = escapeEmoji(comm.Message); } span.appendChild(mmsParts); })); - if (comm.IsMMS && comm.MMSParts?.length > 0) { + if (comm.MMSParts?.length > 0) { mmsParts.style.display = ''; for (let kv of comm.MMSParts) { appendMedia(mmsParts, kv.Key, kv.Value); @@ -1206,39 +1196,16 @@ export default class CustomerCommunication { div.classList.add('item-other'); } else { div.classList.add('item-self'); - const [status, statusmsg] = this.#getMessageStatus(comm); + const [status, text, color, tips] = getMessageStatus(comm, r, this._var); if (status !== -100) { - let statustext; - switch (status) { - case 0: - statustext = r('P_CU_PENDING', 'Pending'); - content.style.backgroundColor = '#ffc107'; - break; - case 1: - statustext = r('P_WO_SENT', 'Sent'); - break; - case 9: - statustext = r('P_MA_FAILED', 'Failed'); - content.style.backgroundColor = '#ffc107'; - break; - case 10: - statustext = r('P_CU_OPTOUT', 'Opt-Out'); - content.style.backgroundColor = '#ffc107'; - break; - case 412: - statustext = r('P_CU_LANDLINE', 'Landline'); - content.style.backgroundColor = '#ffc107'; - break; - default: - statustext = r('P_CU_UNDELIVERED', 'Undelivered'); - content.style.backgroundColor = '#ffc107'; - break; + if (color != null) { + content.style.backgroundColor = color; } const divstatus = createElement('div', div => { div.className = 'item-status'; - div.innerText = statustext; - if (status == -10) { - setTooltip(div, statusmsg); + div.innerText = text; + if (tips != null) { + setTooltip(div, tips); } }); content.appendChild(divstatus); @@ -1255,43 +1222,8 @@ export default class CustomerCommunication { } children[0].style.marginTop = '0'; } - this.#message.replaceChildren(...children); - this.#message.scrollTop = this.#message.scrollHeight - // setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0); - } - - #getMessageStatus(comm) { - let status = -100; // 没有状态,页面上不显示 - const ls = []; - let statusmsg = ''; - if (!comm.StatusIncorrect && comm.Participator?.length > 0) { - for (let p of comm.Participator) { - if (!isEmail(p.CustomerNumber)) { - if (ls.indexOf(p.Status) < 0) { - ls.push(p.Status); - } - if (statusmsg.length > 0) { - statusmsg += '\n'; - } - statusmsg += `${p.CustomerNumber}: `; - const st = ({ - 0: r('P_CU_UNDELIVERED', 'Undelivered'), - 1: r('P_WO_SENT', 'Sent'), - 9: r('P_MA_FAILED', 'Failed') - })[p.Status]; - if (st != null) { - statusmsg += st; - } - else - statusmsg += r('P_MA_XXXXXX', 'Unknown'); - } - } - } - if (ls.length === 1) { - status = ls[0]; - } else if (ls.length > 1) { - status = -10; // 多种状态 - } - return [status, statusmsg]; + this._var.message.replaceChildren(...children); + this._var.message.scrollTop = this._var.message.scrollHeight + // setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0); } } \ No newline at end of file diff --git a/lib/app/communications/follower.js b/lib/app/communications/follower.js index cfd4315..e15241c 100644 --- a/lib/app/communications/follower.js +++ b/lib/app/communications/follower.js @@ -4,11 +4,10 @@ import { nullOrEmpty, r as lang, contains } from "../../utility"; let r = lang; export default class Follower { - #option; - #grid; + _var = {}; constructor(option = {}) { - this.#option = option; + this._var.option = option; const getText = option?.getText; if (typeof getText === 'function') { r = getText; @@ -20,7 +19,7 @@ export default class Follower { const gridContainer = createElement('div', 'follower-grid'); const popup = new Popup({ - onMasking: this.#option.onMasking, + onMasking: this._var.option.onMasking, title, content: createElement('div', 'follower-wrapper', createElement('div', div => div.innerText = r('P_CR_WHODOYOUWANTTORECEIVECUSTOMERNOTIFICATIONS', 'Who do you want to receive customer notifications?')), @@ -31,9 +30,9 @@ export default class Follower { search.addEventListener('input', () => { const key = search.value; if (nullOrEmpty(key)) { - this.#grid.source = this.#option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) }); + this._var.grid.source = this._var.option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) }); } else { - this.#grid.source = this.#option.followers.filter(f => f.Text || f.Email || contains(f.DisplayName, key, true)) + this._var.grid.source = this._var.option.followers.filter(f => f.Text || f.Email || contains(f.DisplayName, key, true)) .sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) }); } }); @@ -45,8 +44,8 @@ export default class Follower { text: r('P_WO_OK', 'OK'), key: 'ok', trigger: () => { - if (typeof this.#option.onOk === 'function') { - return this.#option.onOk.call(this, this.#grid.source.filter(f => f.Email || f.Text)); + if (typeof this._var.option.onOk === 'function') { + return this._var.option.onOk.call(this, this._var.grid.source.filter(f => f.Email || f.Text)); } } }, @@ -76,8 +75,8 @@ export default class Follower { } ]; grid.init(); - grid.source = this.#option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) }); - this.#grid = grid; + grid.source = this._var.option.followers.sort(function (a, b) { return ((b.Text || b.Email) ? 1 : 0) - ((a.Text || a.Email) ? 1 : 0) }); + this._var.grid = grid; return result; } } \ No newline at end of file diff --git a/lib/app/communications/internal.js b/lib/app/communications/internal.js index 3f73206..e3d92fc 100644 --- a/lib/app/communications/internal.js +++ b/lib/app/communications/internal.js @@ -1,33 +1,48 @@ import { createElement, setTooltip, createIcon } from "../../ui"; import { r as lang, nullOrEmpty, escapeHtml, escapeEmoji } from "../../utility"; import { createBox, appendMedia } from "./lib"; -// import { fileSupported, insertFile } from "./lib"; +import { fileSupported, insertFile, getMessageSendTo, getMessageStatus, updateCustomerName } from "./lib"; let r = lang; export default class InternalComment { - #container; - #option; - #enter; - #fileControl; - #file; - #message; + _var = {}; + // _var.container; + // _var.option; + // _var.enter; + // _var.fileControl; + // _var.file; + // _var.message; constructor(opt) { - this.#option = opt ?? {}; + this._var.option = opt ?? {}; const getText = opt?.getText; if (typeof getText === 'function') { r = getText; } } - get text() { return this.#enter?.value } + get text() { return this._var.enter?.value } set text(s) { - const element = this.#enter; + const element = this._var.enter; if (element != null) { element.value = s - s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this.#option.maxLength); - this.#container.querySelector('.message-bar .prompt-count').innerText = s; + s = String(nullOrEmpty(s) ? 0 : val.length) + '/' + String(this._var.option.maxLength); + this._var.container.querySelector('.message-bar .prompt-count').innerText = s; + } + } + + /** + * @param {any} contacts + */ + set contacts(contacts) { + this._var.contacts = contacts; + if (this._var.contactsUpdated !== true && contacts?.length > 0) { + const comments = this._var.comments; + if (comments?.length > 0) { + updateCustomerName(this._var.comments, contacts); + this._var.contactsUpdated = true; + } } } @@ -35,21 +50,21 @@ export default class InternalComment { * @param {boolean} flag */ set loading(flag) { - if (this.#container == null) { + if (this._var.container == null) { return; } - this.#enter.disabled = flag; - this.#container.querySelector('.button-send-message').disabled = flag; - this.#container.querySelector('.button-post-note').disabled = flag; + this._var.enter.disabled = flag; + this._var.container.querySelector('.button-send-message').disabled = flag; + this._var.container.querySelector('.button-post-note').disabled = flag; } - get file() { return this.#file || this.#fileControl?.files?.[0] } + get file() { return this._var.file || this._var.fileControl?.files?.[0] } set file(f) { - this.#fileControl?.remove(); - const label = this.#container.querySelector('.file-selector>.selector-name'); + this._var.fileControl?.remove(); + const label = this._var.container.querySelector('.file-selector>.selector-name'); if (f == null) { - this.#fileControl = null; - this.#file = null; + this._var.fileControl = null; + this._var.file = null; if (label != null) { label.style.display = 'none'; label.innerText = ''; @@ -57,15 +72,15 @@ export default class InternalComment { } } else { if (f instanceof HTMLInputElement) { - this.#fileControl = f; - this.#file = f.files[0]; - const link = this.#container.querySelector('.file-selector>.selector-link'); + this._var.fileControl = f; + this._var.file = f.files[0]; + const link = this._var.container.querySelector('.file-selector>.selector-link'); if (link != null) { link.appendChild(f); } } else { - this.#fileControl = null; - this.#file = f; + this._var.fileControl = null; + this._var.file = f; } if (label != null) { label.style.display = ''; @@ -79,16 +94,17 @@ export default class InternalComment { * @param {boolean} flag */ set readonly(flag) { - this.#option.readonly = flag; - if (this.#container == null) { + this._var.option.readonly = flag; + if (this._var.container == null) { return; } - this.#enter.disabled = flag === true; - this.#container.querySelector('.button-send-message').style.display = flag === true ? 'none' : ''; - this.#container.querySelector('.button-post-note').style.display = flag === true ? 'none' : ''; + this._var.enter.disabled = flag === true; + this._var.container.querySelector('.button-send-message').style.display = flag === true ? 'none' : ''; + this._var.container.querySelector('.button-post-note').style.display = flag === true ? 'none' : ''; } create() { + const option = this._var.option; const container = createBox( createElement('div', null, createElement('div', div => { @@ -97,96 +113,93 @@ export default class InternalComment { }) ), [] ); - const readonly = this.#option.readonly; + const readonly = option.readonly; // enter box const enter = createElement('textarea', 'ui-text'); enter.placeholder = r('P_CU_ENTERCOMMENTHERE', 'Enter Comment Here'); - enter.maxLength = this.#option.maxLength ??= 3000; + enter.maxLength = option.maxLength ??= 3000; enter.addEventListener('input', () => { const val = this.text; - const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(this.#option.maxLength); - this.#container.querySelector('.message-bar .prompt-count').innerText = s; + const s = String(nullOrEmpty(val) ? 0 : val.length) + '/' + String(option.maxLength); + this._var.container.querySelector('.message-bar .prompt-count').innerText = s; }); if (readonly === true) { enter.disabled = true; } - // enter.addEventListener('paste', e => { - // const file = e.clipboardData.files[0]; - // if (file != null) { - // e.preventDefault(); - // this.file = insertFile(container, file, r); - // } - // }); - this.#enter = enter; + enter.addEventListener('paste', e => { + const file = e.clipboardData.files[0]; + if (file != null) { + e.preventDefault(); + this.file = insertFile(container, file, r); + } + }); + this._var.enter = enter; container.appendChild( createElement('div', div => { div.className = 'message-bar'; - // div.addEventListener('dragover', e => { - // if (option.readonly !== true) { - // const item = e.dataTransfer.items[0]; - // if (item?.kind === 'file') { - // e.preventDefault(); - // if (item.type.length > 0 && fileSupported.indexOf(item.type) < 0) { - // e.dataTransfer.dropEffect = 'none'; - // } else { - // e.dataTransfer.dropEffect = 'link'; - // } - // } - // } - // }); - // div.addEventListener('drop', e => { - // if (option.readonly !== true) { - // const file = e.dataTransfer.files[0]; - // if (file != null) { - // e.preventDefault(); - // this.file = insertFile(container, file, r); - // } - // } - // }); + div.addEventListener('dragover', e => { + if (option.readonly !== true) { + const item = e.dataTransfer.items[0]; + if (item?.kind === 'file') { + e.preventDefault(); + if (item.type.length > 0 && fileSupported.indexOf(item.type) < 0) { + e.dataTransfer.dropEffect = 'none'; + } else { + e.dataTransfer.dropEffect = 'link'; + } + } + } + }); + div.addEventListener('drop', e => { + if (option.readonly !== true) { + const file = e.dataTransfer.files[0]; + if (file != null) { + e.preventDefault(); + this.file = insertFile(container, file, r); + } + } + }); }, enter, createElement('div', div => div.style.textAlign = 'right', - createElement('div', selector => { - selector.className = 'file-selector'; - if (this.#option.noMessage === true) { - selector.style.display = 'none'; - } - }, - createElement('div', div => { - div.className = 'selector-link'; - div.style.display = 'none'; - // div.addEventListener('click', () => { - // this.#fileControl?.remove(); - // const file = createElement('input', input => { - // input.type = 'file'; - // input.accept = fileSupported.join(','); - // input.addEventListener('change', () => { - // const file = insertFile(container, input.files?.[0], r); - // if (file != null) { - // this.file = file; - // } - // }); - // }); - // div.appendChild(this.#fileControl = file); - // file.dispatchEvent(new MouseEvent('click')); - // }); - }, - createIcon('fa-regular', 'link') - ), - createElement('span', span => { - span.className = 'selector-name'; - span.style.display = 'none'; - }), - createElement('layer', layer => { - layer.appendChild(createIcon('fa-regular', 'times')); - layer.addEventListener('click', () => this.file = null); - }) + createElement('div', 'customer-left', + createElement('div', 'file-selector', + createElement('div', div => { + div.className = 'selector-link'; + // div.style.display = 'none'; + div.addEventListener('click', () => { + this._var.fileControl?.remove(); + const file = createElement('input', input => { + input.type = 'file'; + input.accept = fileSupported.join(','); + input.addEventListener('change', () => { + const file = insertFile(container, input.files?.[0], r); + if (file != null) { + this.file = file; + } + }); + }); + div.appendChild(this._var.fileControl = file); + file.dispatchEvent(new MouseEvent('click')); + }); + }, + createIcon('fa-regular', 'link') + ), + createElement('span', span => { + span.className = 'selector-name'; + span.style.display = 'none'; + }), + createElement('layer', layer => { + layer.appendChild(createIcon('fa-regular', 'times')); + layer.addEventListener('click', () => this.file = null); + }) + ) ), createElement('div', 'prompt-count'), createElement('button', button => { button.className = 'roundbtn button-send-message'; button.style.backgroundColor = 'rgb(19, 150, 204)'; - if (readonly === true || this.#option.noMessage === true) { + if (readonly === true || option.noMessage === true) { button.style.display = 'none'; } button.appendChild(createIcon('fa-solid', 'paper-plane')); @@ -196,9 +209,9 @@ export default class InternalComment { if (nullOrEmpty(val?.trim())) { return; } - if (typeof this.#option.onAddMessage === 'function') { + if (typeof option.onAddMessage === 'function') { this.loading = true; - this.#option.onAddMessage(this.text); + option.onAddMessage(this.text); } }) }), @@ -216,9 +229,9 @@ export default class InternalComment { if (nullOrEmpty(val?.trim())) { return; } - if (typeof this.#option.onAddComment === 'function') { + if (typeof option.onAddComment === 'function') { this.loading = true; - this.#option.onAddComment(this.text, this.file); + option.onAddComment(this.text, this.file); } }) }) @@ -227,54 +240,77 @@ export default class InternalComment { ); const message = createElement('div', 'list-bar'); - this.#message = message; + this._var.message = message; container.appendChild(message); - return this.#container = container; + return this._var.container = container; } load(data) { const children = []; if (data?.length > 0) { + this._var.comments = data; + if (this._var.contactsUpdated !== true) { + const contacts = this._var.contacts; + if (contacts?.length > 0) { + updateCustomerName(data, contacts); + this._var.contactsUpdated = true; + } + } for (let comment of data) { const div = createElement('div', 'item-div'); - // if (sendto !== '') { - // sendto = r('P_CU_SENDTO_COLON', 'Send To :') + `\n${sendto}`; - // } + const sendto = getMessageSendTo(comment, null, null, r) div.appendChild(createElement('div', div => { div.className = 'item-poster'; - div.innerText = comment.UserName; + div.innerText = comment.Sender; + if (sendto?.length > 0) { + setTooltip(div, sendto); + } })); const content = createElement('div', 'item-content'); const mmsParts = createElement('div', div => div.style.display = 'none'); - content.appendChild(createElement('span', span => span.innerHTML = escapeEmoji(escapeHtml(comment.Comment)), mmsParts)); - if (comment.IsMMS && comment.MMSParts?.length > 0) { + content.appendChild(createElement('span', span => span.innerHTML = escapeHtml(escapeEmoji(comment.Message)), mmsParts)); + if (comment.MMSParts?.length > 0) { mmsParts.style.display = ''; for (let kv of comment.MMSParts) { appendMedia(mmsParts, kv.Key, kv.Value); } } - if (comment.FollowUp?.length > 0) { - div.classList.add('item-sent'); - const sendto = r('P_CU_SENDTO_COLON', 'Send To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n'); - content.appendChild(createElement('div', div => { + // if (comment.FollowUp?.length > 0) { + // div.classList.add('item-sent'); + // const sendto = r('P_CU_SENDTO_COLON', 'Sent To :') + '\r\n' + comment.FollowUp.split(';').join('\r\n'); + // content.appendChild(createElement('div', div => { + // div.className = 'item-status'; + // div.innerText = r('P_WO_SENT', 'Sent'); + // setTooltip(div, sendto); + // })); + // } + const [status, text, color, tips] = getMessageStatus(comment, r, this._var); + if (status !== -100) { + if (color != null) { + content.style.backgroundColor = color; + } + const divstatus = createElement('div', div => { div.className = 'item-status'; - div.innerText = r('P_WO_SENT', 'Sent'); - setTooltip(div, sendto); - })); + div.innerText = text; + if (tips != null) { + setTooltip(div, tips); + } + }); + content.appendChild(divstatus); } div.append( content, createElement('div', div => { div.className = 'item-time'; - div.innerText = comment.SubmitDateStr; + div.innerText = comment.TimeStr; }) ); children.push(div); } children[0].style.marginTop = '0'; } - this.#message.replaceChildren(...children); - this.#message.scrollTop = this.#message.scrollHeight - // setTimeout(() => this.#message.scrollTop = this.#message.scrollHeight, 0); + this._var.message.replaceChildren(...children); + this._var.message.scrollTop = this._var.message.scrollHeight + // setTimeout(() => this._var.message.scrollTop = this._var.message.scrollHeight, 0); } } \ No newline at end of file diff --git a/lib/app/communications/lib.js b/lib/app/communications/lib.js index d8d86e2..34cc3f3 100644 --- a/lib/app/communications/lib.js +++ b/lib/app/communications/lib.js @@ -1,4 +1,5 @@ -import { createElement, setTooltip, showAlert, createPicture, createAudio, createVideo, createFile } from "../../ui"; +import { createElement, setTooltip, showAlert, createPicture, createAudio, createVideo, createFile, createIcon, Popup, Grid, Dropdown } from "../../ui"; +import { global, isEmail } from "../../utility"; export function createBox(title, functions) { const container = createElement('div', 'comm'); @@ -13,22 +14,30 @@ export function createBox(title, functions) { export function appendMedia(container, mimeType, url) { switch (mimeType) { case 'application/pdf': + case '.pdf': container.appendChild(createFile(url, 'file-pdf')); break; case 'application/msword': case 'application/vnd.ms-word': case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': + case '.doc': + case '.docx': container.appendChild(createFile(url, 'file-word')); break; case 'application/vnd.ms-excel': case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': + case '.xls': + case '.xlsx': container.appendChild(createFile(url, 'file-excel')); break; case 'application/vnd.ms-powerpoint': case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': + case '.ppt': + case '.pptx': container.appendChild(createFile(url, 'file-powerpoint')); break; case 'application/smil': + case '.smil': // TODO: ignore smil files // container.appendChild(createFile(url, 'smile')); break; @@ -41,10 +50,18 @@ export function appendMedia(container, mimeType, url) { case 'audio/opus': case 'audio/wav': case 'audio/webm': + case '.aac': + case '.amr': + case '.mp3': + case '.ogg': + case '.opus': + case '.wav': container.appendChild(createAudio(mimeType, url)); break; case 'text/plain': case 'text/x-vcard': + case '.txt': + case '.vcard': container.appendChild(createFile(url, 'id-card')); break; case 'video/3gpp': @@ -54,8 +71,22 @@ export function appendMedia(container, mimeType, url) { case 'video/x-mpeg': case 'video/quicktime': case 'video/webm': + case '.3gp': + case '.3gpp': + case '.mp4': + case '.mpg': + case '.mov': + case '.webm': container.appendChild(createVideo(url)); break; + case '.jpg': + case '.jpeg': + case '.jfif': + case '.png': + case '.gif': + case '.bmp': + container.appendChild(createPicture(url)); + break; default: if (/^image\//.test(mimeType)) { container.appendChild(createPicture(url)); @@ -151,4 +182,295 @@ export function insertFile(container, file, r) { } return file; } -}; \ No newline at end of file +}; + +function getStatusText(status, dict) { + switch (status) { + case 0: + case 1: + case 5: + case 6: + return dict[status]; + case 9: + case 10: + case 412: + return dict[9]; + default: + return dict[9999]; + } +} + +const SymbolDropdown = Symbol.for('ui-dropdown'); + +class DropdownColumn { + static create(col, trigger, parent) { + const drop = new Dropdown({ ...col.dropOptions, parent }); + drop.onselected = trigger; + return drop.create(); + } + + static _getDrop(element) { + const dropGlobal = global[SymbolDropdown]; + if (dropGlobal == null) { + return null; + } + const dropId = element.dataset.dropId; + const drop = dropGlobal[dropId]; + if (drop == null) { + return null; + } + return drop; + } + + static _setValue(source, element, val) { + const data = source?.find(v => v.value === val); + if (data != null) { + val = data.text; + } + super.setValue(element, val); + } + + static setValue(element, val, _item, col) { + if (element.tagName !== 'DIV') { + this._setValue(col.source, element, val); + return; + } + const drop = this._getDrop(element); + if (drop == null) { + return; + } + if (drop.source == null || drop.source.length === 0) { + let source = col.source; + if (source != null) { + drop.source = source; + } + } + drop.select(val, true); + } + + static getValue(e) { + return e.value; + } +} + +export function getMessageStatus(comm, r, _var) { + const messageStatus = { + 0: r('P_CU_PENDING', 'Pending'), + 1: r('P_WO_SENT', 'Sent'), + 5: r('P_CU_DELIVERYCONFIRMED', 'Delivery Confirmed'), + 6: r('P_CU_RESENT', 'Resent'), + 9: r('P_MA_FAILED', 'Failed'), + 9999: r('P_CU_UNKNOWN', 'Unknown') + }; + const knownStatus = [0, 1, 5, 6, 9, 10, 412]; + const okStatus = [1, 5, 6]; + const failedStatus = [9, 10, 412]; + + let status = -100; // 没有状态,页面上不显示 + const ls = []; + const msgs = []; + if (!comm.StatusIncorrect && comm.Participator?.length > 0) { + // if (comm.Id === 433339) { + // comm.Participator[4].Status = 6; + // } + for (let p of comm.Participator) { + if (!isEmail(p.CustomerNumber)) { + if (ls.indexOf(p.Status) < 0) { + ls.push(p.Status); + } + p.statusText = getStatusText(p.Status, messageStatus); + msgs.push(p); + } + } + } + if (ls.length === 1) { + status = ls[0]; + } else if (ls.length > 1) { + // status = -10; // 多种状态 + status = ls + .filter(s => okStatus.indexOf(s) < 0) // ok status + .sort((a, b) => b - a)[0] ?? 1; + } + const statusText = messageStatus[failedStatus.includes(status) ? 9 : status] ?? messageStatus[9999]; + const statusColor = okStatus.includes(status) ? null : '#ffc107'; + const statusUpdatable = _var.option.statusUpdatable; + let statusTips; + if (statusUpdatable !== false || ls.length > 1) { + statusTips = createElement('div', tip => { + for (let i = 0; i < msgs.length; i++) { + tip.appendChild(createElement('div', t => { + const p = msgs[i]; + if (statusUpdatable !== false && p.StatusChanged) { + t.append( + createElement('span', s => s.innerText = `${p.CustomerNumber}: `), + createElement('span', s => { + s.style.color = '#2140fb'; + s.style.cursor = 'pointer'; + s.innerText = p.statusText; + s.addEventListener('click', () => { + if (typeof _var.option.onMessageStatusClicked === 'function') { + _var.option.onMessageStatusClicked(p); + } + }); + }) + ) + } else { + t.innerText = `${p.CustomerNumber}: ${p.statusText}`; + } + })); + } + if (statusUpdatable !== false) { + tip.appendChild(createElement('div', b => { + b.className = 'tip-function-button'; + // setTooltip(b, r('P_CU_UPDATESTATUS', 'Update Status')); + b.addEventListener('click', async () => { + for (let p of comm.Participator) { + switch (p.Status) { + case 0: + case 1: + case 5: + case 6: + p.statusChanged = String(p.Status); + break; + case 9: + case 10: + case 412: + p.statusChanged = '9'; + break; + default: + p.statusChanged = '-1'; + break; + } + } + const gridContainer = createElement('div', 'status-grid'); + const popup = new Popup({ + onMasking: _var.option.onMasking, + title: r('P_CU_UPDATESTATUS', 'Update Status'), + content: createElement('div', wrapper => { + wrapper.className = 'update-status-wrapper'; + wrapper.style.width = '500px'; + }, + gridContainer + ), + buttons: [ + { + text: r('P_WO_OK', 'OK'), + key: 'ok', + trigger: () => { + const changed = msgs.filter(m => { + switch (m.statusChanged) { + case '-1': + return knownStatus.includes(m.Status); + case '9': + return failedStatus.indexOf(m.Status) < 0; + default: + return String(m.Status) !== m.statusChanged; + } + }).map(m => { + let status = Number(m.statusChanged); + if (isNaN(status) || status < 0) { + status = 9999; + } + return { + Id: m.Id, + Status: status + }; + }); + if (typeof _var.option.onUpdateMessageStatus === 'function') { + _var.option.onUpdateMessageStatus(changed); + } + } + }, + { + text: r('P_WO_CANCEL', 'Cancel'), + key: 'cancel' + } + ] + }); + await popup.show(); + const grid = new Grid(gridContainer); + // grid.headerVisible = false; + grid.allowHtml = true; + grid.columns = [ + { + key: 'CustomerNumber', + caption: r('P_JS_NUMBER', 'Number'), + width: 150 + }, + /*{ + key: 'customerName', + caption: r('P_WOS_CUSTOMERNAME', 'Customer Name'), + width: 120 + },*/ + { + key: 'statusText', + caption: r('P_CU_CURRENTSTATUS', 'Current Status'), + width: 155 + }, + { + key: 'statusChanged', + caption: r('P_CU_REVISEDSTATUS', 'Revised Status'), + width: 155, + type: DropdownColumn, + source: [ + { value: '-1', text: messageStatus[9999] }, + { value: '0', text: messageStatus[0] }, + { value: '1', text: messageStatus[1] }, + { value: '5', text: messageStatus[5] }, + { value: '6', text: messageStatus[6] }, + { value: '9', text: messageStatus[9] } + ] + } + ]; + grid.init(); + grid.source = msgs; + }); + }, createIcon('fa-light', 'wave-sine'))); + } + }); + } + return [status, statusText, statusColor, statusTips]; +}; + +export function getMessageSendTo(comm, contacts, followers, r) { + let sendto = ''; + if (!comm.IsReply && comm.OriPhoneNumbers?.length > 0) { + for (let oriph of comm.OriPhoneNumbers) { + let cname; + const email = isEmail(oriph); + if (contacts?.length > 0) { + let c = email ? + contacts.find(c => c.Email === oriph) : + contacts.find(c => c.MobilePhone === oriph); + if (c != null) { + cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`; + } else if (followers?.length > 0) { + c = email ? + followers.find(f => f.Email === oriph) : + followers.find(f => f.MobilePhone === oriph); + if (c != null) { + cname = `${email ? c.Email : c.MobilePhoneDisplayText} - ${c.Name}`; + } + } + } + sendto += (cname ?? oriph) + '\n'; + } + } + if (sendto !== '') { + sendto = r('P_CU_SENDTO_COLON', 'Sent to :') + `\n${sendto}`; + } + return sendto; +} + +export function updateCustomerName(messages, contacts) { + if (messages?.length > 0 && contacts?.length > 0) { + for (let m of messages) { + if (m.Participator?.length > 0) { + for (let p of m.Participator) { + const contact = contacts.filter(c => c.MobilePhoneDisplayText === p.CustomerNumber)[0]; + p.customerName = contact?.Name; + } + } + } + } +} \ No newline at end of file diff --git a/lib/app/communications/style.scss b/lib/app/communications/style.scss index 5ddd1db..eae7209 100644 --- a/lib/app/communications/style.scss +++ b/lib/app/communications/style.scss @@ -1,10 +1,24 @@ @import "../../ui/css/functions/func.scss"; -.ui-popup-mask .wrapper-edit-method { - width: 100%; +.ui-popup-mask { + .wrapper-edit-method { + width: 100%; - .ui-check-wrapper { - padding: 0 28px; + .ui-check-wrapper { + padding: 0 28px; + } + } + + .status-grid, + .contacts-record, + .contacts-wo { + >.ui-grid { + overflow-x: visible; + + >.ui-grid-body { + overflow: visible; + } + } } } @@ -13,7 +27,7 @@ flex-direction: column; width: 320px; background-color: var(--dark-fore-color); - border: 1px solid var(--title-bg-color); + border: 1px solid var(--title-ctrlbg-color); margin-left: 12px; & { @@ -69,7 +83,7 @@ flex: 0 0 auto; padding: 5px 0 5px 10px; color: var(--title-color); - background-color: var(--title-bg-color); + background-color: var(--title-ctrlbg-color); line-height: 24px; display: flex; align-items: center; @@ -132,7 +146,7 @@ flex: 0 0 auto; padding: 4px 0; display: flex; - border-bottom: 1px solid var(--title-bg-color); + border-bottom: 1px solid var(--title-ctrlbg-color); position: relative; >.bar-icon { @@ -253,13 +267,13 @@ .message-bar { flex: 0 0 auto; - border-bottom: 1px solid var(--title-bg-color); + border-bottom: 1px solid var(--title-ctrlbg-color); display: flex; flex-direction: column; >textarea { padding: 10px 10px 0; - border: 1px solid var(--title-bg-color); + border: 1px solid var(--title-ctrlbg-color); border-radius: 5px; height: 70px; resize: none; @@ -274,73 +288,75 @@ >div { padding: 0 10px 10px; - >.customer-name { + >.customer-left { float: left; + text-align: left; - >span { - font-size: var(--font-smaller-size); - } - - >.ui-input { - margin-left: 4px; - width: 150px; - border-top: none; - border-left: none; - border-right: none; - } - } - - >.file-selector { - float: left; - display: inline-flex; - align-items: center; - height: 30px; - - >.selector-link { - cursor: pointer; - display: flex; - - >svg { - width: 16px; - height: 16px; - fill: var(--secondary-link-color); + >.customer-name { + >span { + font-size: var(--font-smaller-size); } - >input { - display: none; + >.ui-input { + margin-left: 4px; + width: 150px; + border-top: none; + border-left: none; + border-right: none; } } - >.selector-name { - max-width: 130px; - padding: 0 20px 0 4px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + >.file-selector { + display: inline-flex; + align-items: center; + height: 30px; - +layer { - display: none; - margin-left: -20px; + >.selector-link { cursor: pointer; + display: flex; >svg { width: 16px; height: 16px; - fill: var(--red-color); + fill: var(--secondary-link-color); } - &:hover { + >input { + display: none; + } + } + + >.selector-name { + max-width: 130px; + padding: 0 20px 0 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + +layer { + display: none; + margin-left: -20px; + cursor: pointer; + + >svg { + width: 16px; + height: 16px; + fill: var(--red-color); + } + + &:hover { + display: flex; + } + } + + &:hover+layer { display: flex; } - } - &:hover+layer { - display: flex; - } - - >.ui-tooltip-wrapper img { - max-width: 120px; - max-height: 80px; + >.ui-tooltip-wrapper img { + max-width: 120px; + max-height: 80px; + } } } } @@ -421,6 +437,25 @@ margin-right: -12px; font-size: .625rem; float: right; + + .ui-tooltip-content .tip-function-button { + text-align: right; + + >svg { + width: 20px; + height: 20px; + cursor: pointer; + border: 1px solid; + border-radius: 10px; + padding: 2px; + box-sizing: border-box; + transition: opacity .12s; + + &:hover { + opacity: .5; + } + } + } } } diff --git a/lib/element.js b/lib/element.js new file mode 100644 index 0000000..8241ca1 --- /dev/null +++ b/lib/element.js @@ -0,0 +1,6 @@ +import "./element/style.scss"; +import ScheduleItem from "./element/schedule"; + +export { + ScheduleItem +} \ No newline at end of file diff --git a/lib/element/schedule.js b/lib/element/schedule.js new file mode 100644 index 0000000..d96edd6 --- /dev/null +++ b/lib/element/schedule.js @@ -0,0 +1,274 @@ +import { Grid, GridColumn, createElement, setTooltip, createIcon, createCheckbox, createRadiobox, showAlert, showConfirm, Popup, Dropdown, validation } from "../ui"; +import { r as lang, nullOrEmpty, formatUrl, escapeEmoji, isEmail, isPhone } from "../utility"; + +let r = lang; + +function datepicker(element) { + if (typeof $?.fn?.datepicker === 'function') { + $(element).datepicker({ + autoHide: true, + format: 'm/dd/yyyy' + }); + } + return element; +} + +export default class ScheduleItem { + _var = {}; + + constructor(opt) { + this._var.option = opt ?? {}; + const getText = opt?.getText; + if (typeof getText === 'function') { + r = getText; + } + } + + get checkOccurOnce() { return this._var.container.querySelector('.schedule-id-box-occur-once>input'); } + get checkOccurEvery() { return this._var.container.querySelector('.schedule-id-box-occur-every>input'); } + get inputOccurOnce() { return this._var.container.querySelector('.schedule-id-occur-once'); } + get inputOccurEvery() { return this._var.container.querySelector('.schedule-id-occur-every'); } + get inputOccurStarting() { return this._var.container.querySelector('.schedule-id-occur-starting') } + get inputOccurEnding() { return this._var.container.querySelector('.schedule-id-occur-ending') } + + changeDailyFrequency(once) { + this.inputOccurOnce.disabled = !once; + this.inputOccurEvery.disabled = once; + this.inputOccurStarting.disabled = once; + this.inputOccurEnding.disabled = once; + } + + getParameters() { + return { + Enabled: this._var.container.querySelector('.schedule-id-enabled>input').checked, + Schedule: { + Frequency: Number(this.dropFrequency.selected.value), + Daily: { + OcurrsOnce: this.checkOccurOnce.checked, + OcurrsOnceAt: this.inputOccurOnce.value, + OcurrsInterval: Number(this.inputOccurEvery.value), + StartingAt: this.inputOccurStarting.value, + EndingAt: this.inputOccurEnding.value + }, + Monday: this._var.container.querySelector('.schedule-id-1>input').checked, + Tuesday: this._var.container.querySelector('.schedule-id-2>input').checked, + Wednesday: this._var.container.querySelector('.schedule-id-3>input').checked, + Thursday: this._var.container.querySelector('.schedule-id-4>input').checked, + Friday: this._var.container.querySelector('.schedule-id-5>input').checked, + Saturday: this._var.container.querySelector('.schedule-id-6>input').checked, + Sunday: this._var.container.querySelector('.schedule-id-7>input').checked, + DayOfMonth: Number(this._var.container.querySelector('.schedule-id-dayofmonth').value), + StartDate: this._var.container.querySelector('.schedule-id-duration-start').value, + EndDate: this._var.container.querySelector('.schedule-id-duration-end').value + } + }; + } + + getDateTime(s) { + if (typeof s === 'string') { + const d = new Date(s); + return isNaN(d.getTime()) ? new Date() : d; + } + return s; + } + + getTimeString(s) { + const d = this.getDateTime(s); + return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0'); + } + + getDateString(s) { + const d = this.getDateTime(s); + return String(d.getMonth() + 1).padStart(2, '0') + '/' + String(d.getDate()).padStart(2, '0') + '/' + String(d.getFullYear()); + } + + setParameters(p) { + this._var.container.querySelector('.schedule-id-enabled>input').checked = p.Enabled; + const schedule = p.Schedule || {}; + this.dropFrequency.select(String(schedule.Frequency)); + let checker = schedule.Daily.OcurrsOnce ? this.checkOccurOnce : this.checkOccurEvery; + checker.checked = true; + checker.dispatchEvent(new Event('change')); + this.inputOccurOnce.value = this.getTimeString(schedule.Daily.OcurrsOnceAt); + this.inputOccurEvery.value = String(schedule.Daily.OcurrsInterval); + this.inputOccurStarting.value = this.getTimeString(schedule.Daily.StartingAt); + this.inputOccurEnding.value = this.getTimeString(schedule.Daily.EndingAt); + this._var.container.querySelector('.schedule-id-1>input').checked = schedule.Monday; + this._var.container.querySelector('.schedule-id-2>input').checked = schedule.Tuesday; + this._var.container.querySelector('.schedule-id-3>input').checked = schedule.Wednesday; + this._var.container.querySelector('.schedule-id-4>input').checked = schedule.Thursday; + this._var.container.querySelector('.schedule-id-5>input').checked = schedule.Friday; + this._var.container.querySelector('.schedule-id-6>input').checked = schedule.Saturday; + this._var.container.querySelector('.schedule-id-7>input').checked = schedule.Sunday; + this._var.container.querySelector('.schedule-id-dayofmonth').value = String(schedule.DayOfMonth); + const start = this.getDateString(schedule.StartDate); + const end = this.getDateString(schedule.EndDate); + if (typeof $?.fn?.datepicker === 'function') { + $(this._var.container.querySelector('.schedule-id-duration-start')).datepicker('setDate', new Date(start)); + $(this._var.container.querySelector('.schedule-id-duration-end')).datepicker('setDate', new Date(end)); + } else { + this._var.container.querySelector('.schedule-id-duration-start').value = start; + this._var.container.querySelector('.schedule-id-duration-end').value = end; + } + } + + create() { + const option = this._var.option; + const drop = new Dropdown({ selected: '0' }); + this.dropFrequency = drop; + drop.source = [ + { value: '0', text: 'Daily' }, + { value: '1', text: 'Weekly' }, + { value: '2', text: 'Monthly' } + ]; + drop.onselected = item => { + container.querySelector('.schedule-item-weekly').style.display = item.value === '1' ? '' : 'none'; + const monthly = item.value === '2'; + container.querySelector('.schedule-item-monthly').style.display = monthly ? '' : 'none'; + if (!monthly) { + const dayofmonth = this._var.container.querySelector('.schedule-id-dayofmonth'); + if (dayofmonth.classList.contains('validation-error')) { + dayofmonth.value = '1'; + } + } + }; + const container = createElement('div', 'schedule-item-container', + createElement('fieldset', 'schedule-item-frequency', + createElement('legend', legend => legend.innerText = 'Frequency'), + createElement('div', 'schedule-item-line', + createElement('span', span => span.innerText = 'Occurs'), + drop.create() + ), + createElement('div', div => { + div.className = 'schedule-item-panel schedule-item-weekly'; + div.style.display = 'none'; + }, + createElement('table', 'schedule-item-table', + createElement('tr', 'schedule-item-tr', + createElement('td', null, createCheckbox({ className: 'schedule-id-1', label: 'Monday' })), + createElement('td', null, createCheckbox({ className: 'schedule-id-3', label: 'Wednesday' })), + createElement('td', null, createCheckbox({ className: 'schedule-id-5', label: 'Friday' })), + createElement('td', null, createCheckbox({ className: 'schedule-id-6', label: 'Saturday' })) + ), + createElement('tr', 'schedule-item-tr', + createElement('td', null, createCheckbox({ className: 'schedule-id-2', label: 'Tuesday' })), + createElement('td', null, createCheckbox({ className: 'schedule-id-4', label: 'Thursday' })), + createElement('td'), + createElement('td', null, createCheckbox({ className: 'schedule-id-7', label: 'Sunday' })) + ) + ) + ), + createElement('div', div => { + div.className = 'schedule-item-panel schedule-item-monthly'; + div.style.display = 'none'; + }, + createElement('div', 'schedule-item-line', + createElement('span', span => span.innerText = 'On day'), + validation( + createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-dayofmonth', i.maxLength = 2 }), + /^([0]?[1-9]|[12][0-9]|[3][01])$/ + ), + createElement('span', span => span.innerText = 'of the month') + ) + ) + ), + createElement('fieldset', 'schedule-item-daily-frequency', + createElement('legend', legend => legend.innerText = 'Daily frequency'), + createElement('div', 'schedule-item-line', + createRadiobox({ + name: 'schedule-daily-occurs', + checked: true, + className: 'schedule-id-box-occur-once', + label: 'Occurs once at', + onchange: e => this.changeDailyFrequency(e.target.checked) + }), + validation( + createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-once', i.maxLength = 5 }), + /^([01][0-9]|[2][0-3]):[0-5][0-9]$/ + ) + ), + createElement('div', 'schedule-item-line schedule-item-line-occur-every', + createRadiobox({ + name: 'schedule-daily-occurs', + className: 'schedule-id-box-occur-every', + label: 'Occurs every', + onchange: e => this.changeDailyFrequency(!e.target.checked) + }), + validation( + createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-every', i.maxLength = 5 }), + /^([0][1-9]+|[1-9][0-9]*)$/ + ), + createElement('span', span => span.innerText = 'minute(s)'), + createElement('div', 'schedule-item-placeholder'), + createElement('div', 'schedule-item-block', + createElement('div', 'scheldule-item-line', + createElement('span', span => span.innerText = 'Starting at'), + validation( + createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-starting', i.maxLength = 5 }), + /^([01][0-9]|[2][0-3]):[0-5][0-9]$/ + ) + ), + createElement('div', 'scheldule-item-line', + createElement('span', span => span.innerText = 'Ending at'), + validation( + createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-occur-ending', i.maxLength = 5 }), + /^([01][0-9]|[2][0-3]):[0-5][0-9]$/ + ) + ) + ) + ) + ), + createElement('fieldset', 'schedule-item-duration', + createElement('legend', legend => legend.innerText = 'Duration'), + createElement('div', 'schedule-item-line schedule-item-line-duration', + createElement('span', span => span.innerText = 'Start date'), + datepicker( + validation( + createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-duration-start', i.maxLength = 10 }), + /^([0]?[1-9]|[1][0-2])\/([0]?[1-9]|[12][0-9]|[3][01])\/[0-9]{4}$/ + ) + ), + createElement('div', 'schedule-item-placeholder'), + createElement('span', span => span.innerText = 'End date'), + datepicker( + validation( + createElement('input', i => { i.type = 'text', i.className = 'ui-input schedule-id-duration-end', i.maxLength = 10 }), + /^([0]?[1-9]|[1][0-2])\/([0]?[1-9]|[12][0-9]|[3][01])\/[0-9]{4}$/ + ) + ) + ), + createElement('div', 'schedule-item-line', + createCheckbox({ className: 'schedule-id-enabled', checked: true, label: 'Enabled' }) + ) + ) + ); + this._var.container = container; + if (option.parameter == null) { + option.parameter = { + Enabled: true, + Schedule: { + Frequency: 0, + Daily: { + OcurrsOnce: true, + OcurrsOnceAt: '00:00', + OcurrsInterval: 60, + StartingAt: '00:00', + EndingAt: '23:00' + }, + Monday: true, + Tuesday: false, + Wednesday: false, + Thursday: false, + Friday: false, + Saturday: false, + Sunday: false, + DayOfMonth: 1, + StartDate: '', + EndDate: '' + } + }; + } + this.setParameters(option.parameter); + return container; + } +} \ No newline at end of file diff --git a/lib/element/style.css b/lib/element/style.css new file mode 100644 index 0000000..6e4356d --- /dev/null +++ b/lib/element/style.css @@ -0,0 +1 @@ +.schedule-item-container fieldset{margin-top:10px;border-width:1px;border-radius:4px;border-color:var(--border-color)}.schedule-item-container fieldset legend,.schedule-item-container fieldset span{font-weight:400;font-size:var(--font-size);padding-left:8px;padding-right:6px;color:var(--color)}.schedule-item-container fieldset .ui-input{line-height:20px}.schedule-item-container fieldset .schedule-item-monthly{margin-top:5px}.schedule-item-container fieldset .schedule-item-monthly .ui-input{width:40px}.schedule-item-container fieldset.schedule-item-daily-frequency .ui-input{vertical-align:top;margin-top:5px}.schedule-item-container fieldset .schedule-item-table{width:100%}.schedule-item-container fieldset .schedule-item-line-occur-every{display:flex;align-items:flex-start}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line{display:flex;align-items:center;margin-top:5px}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line>span{flex:1 1 auto}.schedule-item-container fieldset .schedule-item-line-occur-every>.schedule-item-block>.scheldule-item-line>.ui-input{margin-top:0}.schedule-item-container fieldset .schedule-item-line-occur-every>span{line-height:36px}.schedule-item-container fieldset .schedule-item-line-occur-every .ui-input{width:70px}.schedule-item-container fieldset .schedule-item-line-duration{display:flex;align-items:center;height:36px}.schedule-item-container fieldset .schedule-item-line>.schedule-item-placeholder{flex:1 1 auto}.schedule-item-container .schedule-item-frequency{margin-top:0} \ No newline at end of file diff --git a/lib/element/style.scss b/lib/element/style.scss new file mode 100644 index 0000000..f7a7b35 --- /dev/null +++ b/lib/element/style.scss @@ -0,0 +1,91 @@ +.schedule-item-container { + + fieldset { + margin-top: 10px; + border-width: 1px; + border-radius: 4px; + border-color: var(--border-color); + + legend, + span { + font-weight: 400; + font-size: var(--font-size); + padding-left: 8px; + padding-right: 6px; + color: var(--color); + } + + .ui-input { + line-height: 20px; + height: 20px; + } + + .schedule-item-monthly { + margin-top: 5px; + + .ui-input { + width: 40px; + } + } + + &.schedule-item-daily-frequency .ui-input { + vertical-align: top; + margin-top: 5px; + } + + .schedule-item-table { + width: 100%; + } + + .schedule-item-line-occur-every { + display: flex; + align-items: flex-start; + + >.schedule-item-block>.scheldule-item-line { + display: flex; + align-items: center; + margin-top: 5px; + + >span { + flex: 1 1 auto; + } + + >.ui-input { + margin-top: 0; + } + } + + >span { + line-height: 36px; + } + + .ui-input { + width: 70px; + } + } + + .schedule-item-line-duration { + display: flex; + align-items: center; + height: 36px; + } + + .schedule-item-line { + >.schedule-item-placeholder { + flex: 1 1 auto; + } + } + } + + .schedule-item-frequency { + margin-top: 0; + + >.schedule-item-line { + line-height: 24px; + } + } + + .ui-drop-wrapper>.ui-drop-header { + height: 24px; + } +} \ No newline at end of file diff --git a/lib/ui.js b/lib/ui.js index a7e9d89..8099858 100644 --- a/lib/ui.js +++ b/lib/ui.js @@ -9,6 +9,7 @@ import { Grid } from "./ui/grid/grid"; import { GridColumn, GridInputColumn, GridDropdownColumn, GridCheckboxColumn, GridIconColumn, GridTextColumn } from './ui/grid/column'; import { Popup, createPopup, showAlert, showConfirm } from "./ui/popup"; import { createPicture, createAudio, createVideo, createFile } from './ui/media'; +import { validation } from './ui/extension'; export { createElement, @@ -42,5 +43,7 @@ export { createPicture, createAudio, createVideo, - createFile + createFile, + // extension + validation } diff --git a/lib/ui/css/common.scss b/lib/ui/css/common.scss index d876ece..3e0da0a 100644 --- a/lib/ui/css/common.scss +++ b/lib/ui/css/common.scss @@ -6,6 +6,15 @@ font-family: var(--font-family); @include outborder(); + + &.validation-error { + border-color: var(--red-color); + + &:focus, + &:hover { + border-color: var(--red-color); + } + } } .ui-input { diff --git a/lib/ui/css/grid.css b/lib/ui/css/grid.css new file mode 100644 index 0000000..0eedf6c --- /dev/null +++ b/lib/ui/css/grid.css @@ -0,0 +1 @@ +.ui-grid{position:relative;box-sizing:border-box;overflow:auto}.ui-grid{--cell-hover-bg-color: lightyellow;--header-border-color: #adaba9;--header-bg-color: #fafafa;--header-fore-color: #000;--cell-border-color: #f0f0f0;--cell-fore-color: #333;--dark-border-color: #666;--split-border-color: #b3b3b3;--dragger-bg-color: #fff;--dragger-cursor-color: #333;--row-bg-color: #fff;--row-active-bg-color: #fafafa;--row-selected-bg-color: #e6f2fb;--text-disabled-color: gray;--filter-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05);--filter-transition: transform .12s ease, opacity .24s ease;--row-height: 36px;--header-line-height: 26px;--text-indent: 8px;--loading-size: 40px;--loading-border-radius: 20px;--arrow-size: 4px;--filter-size: 10px;--split-width: 8px;--dragger-size: 20px;--dragger-opacity: .6;--dragger-cursor-size: 4px;--dragger-cursor-pos: -4px;--dragger-cursor-opacity: .3;--header-padding: 4px 12px 4px 8px;--header-filter-padding: 4px 26px 4px 8px;--spacing-s: 4px;--spacing-cell: 6px 4px 6px 8px;--filter-line-height: 30px;--filter-item-padding: 0 4px}.ui-grid:focus,.ui-grid:focus-visible{outline:none}.ui-grid::-webkit-scrollbar{width:8px;height:8px}.ui-grid::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-grid,.ui-grid input[type=text],.ui-grid textarea{font-size:var(--font-size);font-family:var(--font-family)}.ui-grid>.ui-grid-sizer{position:absolute;white-space:nowrap;font-weight:bold;visibility:hidden}.ui-grid>.ui-grid-wrapper{position:relative}.ui-grid>.ui-grid-wrapper>.ui-grid-table{position:absolute;width:100%;min-width:100%;margin:0;border-collapse:collapse;border-spacing:0;table-layout:fixed}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr{color:var(--header-fore-color);position:sticky;top:0;z-index:2}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th{background-color:var(--header-bg-color);-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:0;margin:0;word-wrap:break-word;white-space:normal;position:relative}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th.sticky{position:sticky;z-index:2}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>div{border-bottom:1px solid var(--header-border-color);line-height:var(--header-line-height);min-height:var(--row-height);display:flex;align-items:center;padding:var(--header-padding);box-sizing:border-box}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>div>span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow{width:0;height:0;top:50%;margin-top:calc(0px - var(--arrow-size)/2);right:calc(var(--arrow-size)/2);position:absolute}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow.asc{border-bottom:var(--arrow-size) solid var(--dark-border-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow.desc{border-top:var(--arrow-size) solid var(--dark-border-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow.asc,.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.arrow.desc{border-left:var(--arrow-size) solid rgba(0,0,0,0);border-right:var(--arrow-size) solid rgba(0,0,0,0)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter{width:var(--filter-size);height:var(--filter-size);top:50%;margin-top:calc(0px - var(--filter-size)/2);right:calc(var(--arrow-size)*2 + 4px);position:absolute;display:flex}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter>svg{width:100%;height:100%;fill:var(--color);opacity:.2;transition:opacity .12s ease}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter>svg:hover{opacity:.8}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter.hover>svg{opacity:.8}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.filter.active>svg{opacity:1}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.spliter{position:absolute;height:100%;top:0;right:calc(0px - var(--split-width)/2);width:var(--split-width);cursor:ew-resize;z-index:1}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.spliter::after{content:"";height:100%;width:1px;display:block;margin:0 auto;transition:background-color .12s ease}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.spliter:hover::after{background-color:var(--split-border-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger{position:absolute;left:0;top:0;min-width:var(--dragger-size);height:100%;background-color:var(--dragger-bg-color);opacity:var(--dragger-opacity);display:none}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor{position:absolute;top:0;height:100%;border:1px solid var(--dragger-cursor-color);box-sizing:border-box;margin-left:0;opacity:var(--dragger-cursor-opacity);display:none;transition:left .12s ease}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor::before{top:-1px;border-top:var(--dragger-cursor-size) solid}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor::after{bottom:-1px;border-bottom:var(--dragger-cursor-size) solid}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor::before,.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th>.dragger-cursor::after{content:"";position:absolute;left:var(--dragger-cursor-pos);border-left:var(--dragger-cursor-size) solid rgba(0,0,0,0);border-right:var(--dragger-cursor-size) solid rgba(0,0,0,0)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>thead tr>th.header-filter>div{padding:var(--header-filter-padding)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody{color:var(--cell-fore-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row{line-height:var(--line-height);white-space:nowrap;background-color:var(--row-bg-color);border-bottom:1px solid var(--cell-border-color);box-sizing:border-box}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row:hover{background-color:var(--row-active-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row:hover>td.sticky{background-color:var(--row-active-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row.selected{background-color:var(--row-selected-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row.selected>td.sticky{background-color:var(--row-selected-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td{padding:0}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td.sticky{position:sticky;z-index:1;background-color:var(--row-bg-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>span{padding:var(--spacing-cell);display:block;overflow:hidden;text-overflow:ellipsis;white-space:pre}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text],.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea{border:none;box-sizing:border-box;width:100%;padding:0}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text]:focus,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text]:focus-visible,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea:focus,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea:focus-visible{outline:none}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text]:disabled,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea:disabled{color:var(--text-disabled-color)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>input[type=text]{height:var(--row-height);text-indent:var(--text-indent)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea{resize:none;line-height:var(--line-height);display:block;padding:var(--spacing-cell);white-space:nowrap}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea::-webkit-scrollbar{width:8px;height:8px}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td>textarea::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-check-wrapper{display:flex;justify-content:center}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-check-wrapper .ui-check-inner,.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-check-wrapper .ui-check-inner>svg{transition:none}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-drop-wrapper{height:var(--row-height);width:100%;display:flex;flex-direction:column}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-header{border:none;height:100%}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-header>.ui-drop-text{padding:var(--spacing-cell)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-box{top:calc(var(--row-height) + 2px)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-box.slide-up{top:unset;bottom:calc(var(--row-height) + 2px)}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon{display:flex;cursor:pointer;justify-content:center;align-items:center;position:relative}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon>svg{width:16px;height:16px;fill:var(--primary-color);transition:opacity .12s ease}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon:hover>svg{opacity:.4}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon.disabled{cursor:unset}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody>.ui-grid-row>td .col-icon.disabled>svg{fill:var(--header-border-color);opacity:unset}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody .ui-grid-hover-holder{box-sizing:border-box;position:absolute;line-height:var(--line-height);padding:var(--spacing-cell);background-color:var(--cell-hover-bg-color);white-space:pre;display:flex;align-items:center;visibility:hidden;opacity:0;transition:visibility 0s linear .12s,opacity .12s ease}.ui-grid>.ui-grid-wrapper>.ui-grid-table>tbody .ui-grid-hover-holder.active{visibility:visible;opacity:1}.ui-grid>.ui-grid-loading{position:absolute;top:0;right:0;bottom:0;left:0;visibility:hidden;opacity:0;transition:visibility 0s linear .12s,opacity .12s ease;background-color:var(--loading-bg-color);display:flex;justify-content:center;align-items:center;z-index:1}.ui-grid>.ui-grid-loading>div{background-color:var(--loading-fore-color);border-radius:var(--loading-border-radius)}.ui-grid>.ui-grid-loading>div>svg{width:var(--loading-size);height:var(--loading-size);padding:20px;animation:loading-spinner 1.2s infinite linear}.ui-grid>.filter-panel{position:absolute;width:200px;height:300px;box-shadow:var(--filter-shadow);transition:var(--filter-transition);background-color:var(--bg-color);transform:scaleY(0);transform-origin:top;opacity:0;display:flex;flex-direction:column;z-index:3}.ui-grid>.filter-panel.active{transform:scaleY(1);opacity:1}.ui-grid>.filter-panel>.filter-search-holder{position:relative;margin:8px 8px 4px}.ui-grid>.filter-panel>.filter-search-holder>.filter-search-box{box-sizing:border-box;text-indent:16px;width:100%;font-size:var(--font-smaller-size);height:var(--line-height);line-height:var(--line-height)}.ui-grid>.filter-panel>.filter-search-holder>svg{position:absolute;width:12px;height:12px;top:calc(50% - 6px);left:4px;fill:var(--color);cursor:text}.ui-grid>.filter-panel>.filter-item-list{flex:1 1 auto;overflow-y:auto;overflow-x:hidden;position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none}.ui-grid>.filter-panel>.filter-item-list::-webkit-scrollbar{width:8px;height:8px}.ui-grid>.filter-panel>.filter-item-list::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-grid>.filter-panel>.filter-item-list>.filter-content{position:absolute;width:100%}.ui-grid>.filter-panel>.filter-item-list .filter-item{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;padding:var(--filter-item-padding)}.ui-grid>.filter-panel>.filter-item-list .filter-item:hover{background-color:var(--hover-bg-color)}.ui-grid>.filter-panel>.filter-item-list .filter-item .ui-check-wrapper{height:var(--filter-line-height);display:flex}.ui-grid>.filter-panel>.filter-item-list .filter-item .ui-check-wrapper .ui-check-inner+*{font-size:var(--font-smaller-size)}.ui-grid>.filter-panel>.filter-function{display:flex;justify-content:flex-end;padding:4px}.ui-grid>.filter-panel>.filter-function>button{box-sizing:border-box;margin-right:10px;min-width:40px;height:var(--filter-line-height);border:none;background-color:rgba(0,0,0,0);cursor:pointer;border-radius:0;transition:background-color .12s ease}.ui-grid>.filter-panel>.filter-function>button:focus,.ui-grid>.filter-panel>.filter-function>button:focus-visible{outline:none}.ui-grid>.filter-panel>.filter-function>button:hover{background-color:var(--hover-bg-color)} \ No newline at end of file diff --git a/lib/ui/css/grid.scss b/lib/ui/css/grid.scss index 587904e..4ad108f 100644 --- a/lib/ui/css/grid.scss +++ b/lib/ui/css/grid.scss @@ -3,10 +3,7 @@ .ui-grid { position: relative; box-sizing: border-box; - display: flex; - flex-direction: column; - overflow: visible; - overflow-x: hidden; + overflow: auto; & { --cell-hover-bg-color: lightyellow; @@ -52,6 +49,7 @@ } @include outline(); + @include scrollbar(); &, input[type="text"], @@ -67,321 +65,338 @@ visibility: hidden; } - >.ui-grid-header { - width: 100%; - min-width: 100%; - margin: 0; - border-bottom: 1px solid var(--header-border-color); - background-color: var(--header-bg-color); - color: var(--header-fore-color); - user-select: none; - border-collapse: collapse; - border-spacing: 0; - table-layout: fixed; + >.ui-grid-wrapper { + position: relative; - tr { - position: relative; + >.ui-grid-table { + position: absolute; + width: 100%; + min-width: 100%; + margin: 0; + border-collapse: collapse; + border-spacing: 0; + table-layout: fixed; - >th { - padding: 0; - margin: 0; - word-wrap: break-word; - white-space: normal; - position: relative; + >thead { + tr { + color: var(--header-fore-color); + position: sticky; + top: 0; + z-index: 2; - >div { - line-height: var(--header-line-height); - min-height: var(--row-height); - display: flex; - align-items: center; - padding: var(--header-padding); + >th { + background-color: var(--header-bg-color); + user-select: none; + padding: 0; + margin: 0; + word-wrap: break-word; + white-space: normal; + position: relative; + + &.sticky { + position: sticky; + z-index: 2; + } + + >div { + border-bottom: 1px solid var(--header-border-color); + line-height: var(--header-line-height); + min-height: var(--row-height); + display: flex; + align-items: center; + padding: var(--header-padding); + box-sizing: border-box; + // overflow-x: hidden; + + >span { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } + + >.arrow { + width: 0; + height: 0; + top: 50%; + margin-top: calc(0px - var(--arrow-size) / 2); + right: calc(var(--arrow-size) / 2); + position: absolute; + + &.asc { + border-bottom: var(--arrow-size) solid var(--dark-border-color); + } + + &.desc { + border-top: var(--arrow-size) solid var(--dark-border-color); + } + + &.asc, + &.desc { + border-left: var(--arrow-size) solid transparent; + border-right: var(--arrow-size) solid transparent; + } + } + + >.filter { + width: var(--filter-size); + height: var(--filter-size); + top: 50%; + margin-top: calc(0px - var(--filter-size) / 2); + right: calc(var(--arrow-size) * 2 + 4px); + position: absolute; + display: flex; + + >svg { + width: 100%; + height: 100%; + fill: var(--color); + opacity: .2; + transition: opacity .12s ease; + + &:hover { + opacity: .8; + } + } + + &.hover>svg { + opacity: .8; + } + + &.active>svg { + opacity: 1; + } + } + + >.spliter { + position: absolute; + height: 100%; + top: 0; + right: calc(0px - var(--split-width) /2); + width: var(--split-width); + cursor: ew-resize; + z-index: 1; + + &::after { + content: ''; + height: 100%; + width: 1px; + display: block; + margin: 0 auto; + transition: background-color .12s ease; + } + + &:hover::after { + background-color: var(--split-border-color); + } + } + + >.dragger { + position: absolute; + left: 0; + top: 0; + min-width: var(--dragger-size); + height: 100%; + background-color: var(--dragger-bg-color); + opacity: var(--dragger-opacity); + display: none; + } + + >.dragger-cursor { + position: absolute; + top: 0; + height: 100%; + border: 1px solid var(--dragger-cursor-color); + box-sizing: border-box; + margin-left: 0; + opacity: var(--dragger-cursor-opacity); + display: none; + transition: left .12s ease; + + &::before { + top: -1px; + border-top: var(--dragger-cursor-size) solid; + } + + &::after { + bottom: -1px; + border-bottom: var(--dragger-cursor-size) solid; + } + + &::before, + &::after { + content: ''; + position: absolute; + left: var(--dragger-cursor-pos); + border-left: var(--dragger-cursor-size) solid transparent; + border-right: var(--dragger-cursor-size) solid transparent; + } + } + + &.header-filter>div { + padding: var(--header-filter-padding); + } + } + } + } + + >tbody { + color: var(--cell-fore-color); + + >.ui-grid-row { + line-height: var(--line-height); + white-space: nowrap; + background-color: var(--row-bg-color); + border-bottom: 1px solid var(--cell-border-color); box-sizing: border-box; - // overflow-x: hidden; - >span { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - } + &:hover { + background-color: var(--row-active-bg-color); - >.arrow { - width: 0; - height: 0; - top: 50%; - margin-top: calc(0px - var(--arrow-size) / 2); - right: calc(var(--arrow-size) / 2); - position: absolute; - - &.asc { - border-bottom: var(--arrow-size) solid var(--dark-border-color); - } - - &.desc { - border-top: var(--arrow-size) solid var(--dark-border-color); - } - - &.asc, - &.desc { - border-left: var(--arrow-size) solid transparent; - border-right: var(--arrow-size) solid transparent; - } - } - - >.filter { - width: var(--filter-size); - height: var(--filter-size); - top: 50%; - margin-top: calc(0px - var(--filter-size) / 2); - right: calc(var(--arrow-size) * 2 + 4px); - position: absolute; - display: flex; - - >svg { - width: 100%; - height: 100%; - fill: var(--color); - opacity: .2; - transition: opacity .12s ease; - - &:hover { - opacity: .8; + >td.sticky { + background-color: var(--row-active-bg-color); } } - &.hover>svg { - opacity: .8; + &.selected { + background-color: var(--row-selected-bg-color); + + >td.sticky { + background-color: var(--row-selected-bg-color); + } } - &.active>svg { + >td { + padding: 0; + + &.sticky { + position: sticky; + z-index: 1; + background-color: var(--row-bg-color); + } + + >span { + padding: var(--spacing-cell); + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: pre; + } + + >input[type="text"], + >textarea { + border: none; + box-sizing: border-box; + width: 100%; + padding: 0; + + @include outline(); + + &:disabled { + color: var(--text-disabled-color); + } + } + + >input[type="text"] { + height: var(--row-height); + text-indent: var(--text-indent); + } + + >textarea { + resize: none; + line-height: var(--line-height); + display: block; + padding: var(--spacing-cell); + white-space: nowrap; + @include scrollbar(); + } + + .ui-check-wrapper { + display: flex; + justify-content: center; + + .ui-check-inner { + + &, + >svg { + transition: none; + } + } + } + + .ui-drop-wrapper { + height: var(--row-height); + width: 100%; + display: flex; + flex-direction: column; + + >.ui-drop-header { + border: none; + height: 100%; + + >.ui-drop-text { + padding: var(--spacing-cell); + } + } + + >.ui-drop-box { + top: calc(var(--row-height) + 2px); + + &.slide-up { + top: unset; + bottom: calc(var(--row-height) + 2px); + } + } + } + + .col-icon { + display: flex; + cursor: pointer; + justify-content: center; + align-items: center; + position: relative; + + >svg { + width: 16px; + height: 16px; + fill: var(--primary-color); + transition: opacity .12s ease; + } + + &:hover>svg { + opacity: .4; + } + + &.disabled { + cursor: unset; + + >svg { + fill: var(--header-border-color); + opacity: unset; + } + } + } + } + } + + .ui-grid-hover-holder { + box-sizing: border-box; + position: absolute; + line-height: var(--line-height); + padding: var(--spacing-cell); + background-color: var(--cell-hover-bg-color); + white-space: pre; + display: flex; + align-items: center; + visibility: hidden; + opacity: 0; + transition: visibility 0s linear .12s, opacity .12s ease; + + &.active { + visibility: visible; opacity: 1; } } - - >.spliter { - position: absolute; - height: 100%; - top: 0; - right: calc(0px - var(--split-width) /2); - width: var(--split-width); - cursor: ew-resize; - z-index: 1; - - &::after { - content: ''; - height: 100%; - width: 1px; - display: block; - margin: 0 auto; - transition: background-color .12s ease; - } - - &:hover::after { - background-color: var(--split-border-color); - } - } - - >.dragger { - position: absolute; - left: 0; - top: 0; - min-width: var(--dragger-size); - height: 100%; - background-color: var(--dragger-bg-color); - opacity: var(--dragger-opacity); - display: none; - } - - >.dragger-cursor { - position: absolute; - top: 0; - height: 100%; - border: 1px solid var(--dragger-cursor-color); - box-sizing: border-box; - margin-left: 0; - opacity: var(--dragger-cursor-opacity); - display: none; - transition: left .12s ease; - - &::before { - top: -1px; - border-top: var(--dragger-cursor-size) solid; - } - - &::after { - bottom: -1px; - border-bottom: var(--dragger-cursor-size) solid; - } - - &::before, - &::after { - content: ''; - position: absolute; - left: var(--dragger-cursor-pos); - border-left: var(--dragger-cursor-size) solid transparent; - border-right: var(--dragger-cursor-size) solid transparent; - } - } - - &.header-filter>div { - padding: var(--header-filter-padding); - } - } - } - } - - >.ui-grid-body { - flex: 1 1 auto; - overflow: auto; - color: var(--cell-fore-color); - @include scrollbar(); - - .ui-grid-body-content { - position: absolute; - min-width: 100%; - table-layout: fixed; - border-collapse: collapse; - border-spacing: 0; - - >.ui-grid-row { - line-height: var(--line-height); - white-space: nowrap; - background-color: var(--row-bg-color); - border-bottom: 1px solid var(--cell-border-color); - box-sizing: border-box; - - &:hover { - background-color: var(--row-active-bg-color); - } - - &.selected { - background-color: var(--row-selected-bg-color); - } - - >td { - padding: 0; - - >span { - padding: var(--spacing-cell); - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: pre; - } - - >input[type="text"], - >textarea { - border: none; - box-sizing: border-box; - width: 100%; - padding: 0; - - @include outline(); - - &:disabled { - color: var(--text-disabled-color); - } - } - - >input[type="text"] { - height: var(--row-height); - text-indent: var(--text-indent); - } - - >textarea { - resize: none; - line-height: var(--line-height); - display: block; - padding: var(--spacing-cell); - white-space: nowrap; - @include scrollbar(); - } - - .ui-check-wrapper { - display: flex; - justify-content: center; - - .ui-check-inner { - - &, - >svg { - transition: none; - } - } - } - - .ui-drop-wrapper { - height: var(--row-height); - width: 100%; - display: flex; - flex-direction: column; - - >.ui-drop-header { - border: none; - height: 100%; - - >.ui-drop-text { - padding: var(--spacing-cell); - } - } - - >.ui-drop-box { - top: calc(var(--row-height) + 2px); - - &.slide-up { - top: unset; - bottom: calc(var(--row-height) + 2px); - } - } - } - - .col-icon { - display: flex; - cursor: pointer; - justify-content: center; - align-items: center; - position: relative; - - >svg { - width: 16px; - height: 16px; - fill: var(--primary-color); - transition: opacity .12s ease; - } - - &:hover>svg { - opacity: .4; - } - - &.disabled { - cursor: unset; - - >svg { - fill: var(--header-border-color); - opacity: unset; - } - } - } - } - } - } - - .ui-grid-hover-holder { - box-sizing: border-box; - position: absolute; - line-height: var(--line-height); - padding: var(--spacing-cell); - background-color: var(--cell-hover-bg-color); - white-space: pre; - display: flex; - align-items: center; - visibility: hidden; - opacity: 0; - transition: visibility 0s linear .12s, opacity .12s ease; - - &.active { - visibility: visible; - opacity: 1; } } } @@ -423,6 +438,7 @@ opacity: 0; display: flex; flex-direction: column; + z-index: 3; &.active { transform: scaleY(1); diff --git a/lib/ui/css/popup.scss b/lib/ui/css/popup.scss index 31009ac..05f63cb 100644 --- a/lib/ui/css/popup.scss +++ b/lib/ui/css/popup.scss @@ -48,7 +48,7 @@ $buttonHeight: 28px; border-radius: var(--corner-radius) var(--corner-radius) 0 0; line-height: $headerLineHeight; user-select: none; - background-color: var(--title-bg-color); + background-color: var(--title-ctrlbg-color); color: var(--title-color); display: flex; align-items: center; @@ -214,7 +214,7 @@ $buttonHeight: 28px; text-align: center; cursor: pointer; user-select: none; - background-color: var(--title-bg-color); + background-color: var(--title-ctrlbg-color); transition: opacity .12s ease; &:focus, diff --git a/lib/ui/dropdown.js b/lib/ui/dropdown.js index 1ebf661..025dd76 100644 --- a/lib/ui/dropdown.js +++ b/lib/ui/dropdown.js @@ -82,17 +82,18 @@ function filterSource(searchkeys, textkey, key, source) { } export class Dropdown { - #options; + _var = {}; + // _var.options; - #wrapper; - #container; - #label; + // _var.wrapper; + // _var.container; + // _var.label; - #allChecked; - #source; - #lastSelected; - #selected; - #selectedList; + // _var.allChecked; + // _var.source; + // _var.lastSelected; + // _var.selected; + // _var.selectedList; sourceFilter; onselectedlist; @@ -105,18 +106,18 @@ export class Dropdown { options.valuekey ??= 'value'; options.htmlkey ??= 'html'; options.maxlength ??= 500; - this.#options = options; + this._var.options = options; } create() { - const options = this.#options; + const options = this._var.options; // wrapper const wrapper = createElement('div', 'ui-drop-wrapper'); const dropId = String(Math.random()).substring(2); wrapper.dataset.dropId = dropId; dropdownGlobal[dropId] = this; - this.#wrapper = wrapper; + this._var.wrapper = wrapper; // header const header = createElement('div', 'ui-drop-header'); @@ -131,8 +132,8 @@ export class Dropdown { if (up || down) { const source = this.source; const count = source.length; - const valuekey = this.#options.valuekey; - let index = source?.indexOf(this.#selected); + const valuekey = this._var.options.valuekey; + let index = source?.indexOf(this._var.selected); if (isNaN(index) || index < -1) { index = -1; } else if (index >= count) { @@ -158,19 +159,19 @@ export class Dropdown { this.select(target); } } else if (e.key === 'Tab') { - this.#dropdown(false); + this._dropdown(false); } }); header.addEventListener('click', () => { if (this.disabled) { return; } - const active = this.#expanded; - const label = this.#label; + const active = this._expanded; + const label = this._var.label; if (active && label.ownerDocument.activeElement === label) { return; } - this.#dropdown(!active); + this._dropdown(!active); if (!active && typeof this.onexpanded === 'function') { setTimeout(() => this.onexpanded(), 120); } @@ -187,21 +188,21 @@ export class Dropdown { label.addEventListener('input', e => { const key = e.target.value.toLowerCase(); const source = filterSource(options.searchkeys, options.textkey, key, this.source); - this.#filllist(source); - this.#container.classList.add('active'); + this._filllist(source); + this._var.container.classList.add('active'); }); label.addEventListener('blur', e => this.select(e.target.value)); - label.addEventListener('mousedown', e => this.#expanded && e.stopPropagation()); + label.addEventListener('mousedown', e => this._expanded && e.stopPropagation()); } else { isPositive(options.tabIndex) && header.setAttribute('tabindex', options.tabIndex); label = createElement('label', 'ui-drop-text'); } - this.#label = label; + this._var.label = label; if (options.multiselect) { if (Array.isArray(options.selectedlist)) { this.selectlist(options.selectedlist, true); } else { - this.#allChecked = true; + this._var.allChecked = true; label.innerText = r('allItem', '( All )'); } } else if (options.selected != null) { @@ -214,23 +215,23 @@ export class Dropdown { return wrapper; } - get multiselect() { return this.#options.multiselect } + get multiselect() { return this._var.options.multiselect } - get disabled() { return this.#wrapper == null || this.#wrapper.querySelector('.ui-drop-header.disabled') != null } + get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null } set disabled(flag) { - if (this.#wrapper == null) { + if (this._var.wrapper == null) { return; } if (flag) { - this.#wrapper.querySelector('.ui-drop-header').classList.add('disabled'); + this._var.wrapper.querySelector('.ui-drop-header').classList.add('disabled'); } else { - this.#wrapper.querySelector('.ui-drop-header').classList.remove('disabled'); + this._var.wrapper.querySelector('.ui-drop-header').classList.remove('disabled'); } } get source() { - let source = this.#source; + let source = this._var.source; if (source == null || !Array.isArray(source)) { if (typeof this.sourceFilter === 'function') { source = this.sourceFilter(); @@ -238,7 +239,7 @@ export class Dropdown { if (!Array.isArray(source)) { source = []; } - this.#source = source; + this._var.source = source; } return source; } @@ -247,59 +248,59 @@ export class Dropdown { if (!Array.isArray(list)) { return; } - this.#source = list; - if (this.#expanded) { - setTimeout(() => this.#dropdown(), 120); + this._var.source = list; + if (this._expanded) { + setTimeout(() => this._dropdown(), 120); } } - get selected() { return this.#selected } + get selected() { return this._var.selected } - get selectedlist() { return this.#selectedList || [] } + get selectedlist() { return this._var.selectedList || [] } select(selected, silence) { - if (this.#lastSelected === selected) { + if (this._var.lastSelected === selected) { return false; } - this.#lastSelected = selected; - const valuekey = this.#options.valuekey; - const textkey = this.#options.textkey; - const htmlkey = this.#options.htmlkey; + this._var.lastSelected = selected; + const valuekey = this._var.options.valuekey; + const textkey = this._var.options.textkey; + const htmlkey = this._var.options.htmlkey; let item = this.source.find(it => it[valuekey] === selected); - if (this.#options.input) { + if (this._var.options.input) { if (item == null) { item = { [valuekey]: selected }; } - this.#label.value = selected; + this._var.label.value = selected; } else { - const expanded = this.#expanded; + const expanded = this._expanded; if (expanded) { - this.#container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected')); + this._var.container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected')); } if (item == null) { - this.#selected = null; - this.#label.innerText = ' '; + this._var.selected = null; + this._var.label.innerText = ' '; return false; } const html = item[htmlkey]; if (html instanceof HTMLElement) { - this.#label.replaceChildren(html.cloneNode(true)); + this._var.label.replaceChildren(html.cloneNode(true)); } else { let text = item[textkey]; if (nullOrEmpty(text)) { text = ' '; } - this.#label.innerText = text; + this._var.label.innerText = text; } if (expanded) { const val = selected.replace(/"/g, '\\"'); - const li = this.#container.querySelector(`li[data-value="${val}"]`); + const li = this._var.container.querySelector(`li[data-value="${val}"]`); if (li != null) { li.classList.add('selected'); } } } - this.#selected = item; + this._var.selected = item; if (!silence && typeof this.onselected === 'function') { this.onselected(item); } @@ -307,9 +308,9 @@ export class Dropdown { selectlist(selectedlist, silence) { const source = this.source; - const valuekey = this.#options.valuekey; - const textkey = this.#options.textkey; - const htmlkey = this.#options.htmlkey; + const valuekey = this._var.options.valuekey; + const textkey = this._var.options.textkey; + const htmlkey = this._var.options.htmlkey; const itemlist = selectedlist.map(v => { let item = source.find(it => it[valuekey] === v); if (item == null) { @@ -318,22 +319,22 @@ export class Dropdown { return item; }); if (itemlist.length === 0) { - this.#selectedList = null; - this.#label.innerText = none; + this._var.selectedList = null; + this._var.label.innerText = none; return false; } - selectItems(this.#label, itemlist, htmlkey, textkey); - this.#selectedList = itemlist; + selectItems(this._var.label, itemlist, htmlkey, textkey); + this._var.selectedList = itemlist; if (!silence && typeof this.onselectedlist === 'function') { this.onselectedlist(itemlist); } } - get #expanded() { return this.#container?.classList?.contains('active') } + get _expanded() { return this._var.container?.classList?.contains('active') } - #dropdown(flag = true) { - const options = this.#options; - let panel = this.#container; + _dropdown(flag = true) { + const options = this._var.options; + let panel = this._var.container; if (panel == null) { panel = createElement('div', 'ui-drop-box'); // search box @@ -346,7 +347,7 @@ export class Dropdown { input.addEventListener('input', e => { const key = e.target.value.toLowerCase(); const source = filterSource(options.searchkeys, options.textkey, key, this.source); - this.#filllist(source); + this._filllist(source); }) search.append(input, createIcon('fa-light', 'search')); panel.appendChild(search); @@ -369,8 +370,12 @@ export class Dropdown { }); } panel.appendChild(list); - this.#container = panel; - this.#wrapper.appendChild(panel); + this._var.container = panel; + if (options.holder instanceof HTMLElement) { + options.holder.appendChild(panel); + } else { + this._var.wrapper.appendChild(panel); + } } if (flag) { let source = this.source; @@ -380,11 +385,11 @@ export class Dropdown { source = filterSource(options.searchkeys, options.textkey, search.value, source); } } - this.#filllist(source); + this._filllist(source); // slide direction if (!options.slidefixed) { let parent = options.parent ?? document.body; - let p = this.#wrapper; + let p = this._var.wrapper; let top = p.offsetTop; while ((p = p.parentElement) != null && p !== parent) { top -= p.scrollTop; @@ -401,11 +406,11 @@ export class Dropdown { } } - #filllist(source) { - const list = this.#container.querySelector('.ui-drop-list'); + _filllist(source) { + const list = this._var.container.querySelector('.ui-drop-list'); list.replaceChildren(); const multiselect = this.multiselect; - const allchecked = this.#allChecked; + const allchecked = this._var.allChecked; if (multiselect) { list.appendChild( createElement('li', null, @@ -413,15 +418,15 @@ export class Dropdown { label: r('allItem', '( All )'), checked: allchecked, customAttributes: { 'isall': '1' }, - onchange: e => this.#triggerselect(e.target) + onchange: e => this._triggerselect(e.target) }) ) ); } // TODO: virtual mode - const valuekey = this.#options.valuekey; - const textkey = this.#options.textkey; - const htmlkey = this.#options.htmlkey; + const valuekey = this._var.options.valuekey; + const textkey = this._var.options.textkey; + const htmlkey = this._var.options.htmlkey; const selected = this.selected; const selectedlist = this.selectedlist; let scrolled; @@ -448,7 +453,7 @@ export class Dropdown { 'class': 'dataitem', 'data-value': val }, - onchange: e => this.#triggerselect(e.target) + onchange: e => this._triggerselect(e.target) }); li.appendChild(box); } else { @@ -469,43 +474,43 @@ export class Dropdown { } } - #triggerselect(checkbox) { + _triggerselect(checkbox) { let list; - const valuekey = this.#options.valuekey; - const textkey = this.#options.textkey; - const htmlkey = this.#options.htmlkey; + const valuekey = this._var.options.valuekey; + const textkey = this._var.options.textkey; + const htmlkey = this._var.options.htmlkey; if (checkbox.getAttribute('isall') === '1') { - const allchecked = this.#allChecked = checkbox.checked; - const boxes = this.#container.querySelectorAll('input.dataitem'); + const allchecked = this._var.allChecked = checkbox.checked; + const boxes = this._var.container.querySelectorAll('input.dataitem'); boxes.forEach(box => box.checked = allchecked); list = []; } else if (checkbox.checked) { - if (this.#container.querySelectorAll('input.dataitem:not(:checked)').length === 0) { - this.#allChecked = true; - this.#container.querySelector('input[isall="1"]').checked = true; + if (this._var.container.querySelectorAll('input.dataitem:not(:checked)').length === 0) { + this._var.allChecked = true; + this._var.container.querySelector('input[isall="1"]').checked = true; list = []; } else { const source = this.source; - list = [...this.#container.querySelectorAll('input.dataitem:checked')] + list = [...this._var.container.querySelectorAll('input.dataitem:checked')] .map(c => source.find(it => it[valuekey] === c.dataset.value)) .filter(it => it != null); } } else { const val = checkbox.dataset.value; - if (this.#allChecked) { - this.#allChecked = false; - this.#container.querySelector('input[isall="1"]').checked = false; + if (this._var.allChecked) { + this._var.allChecked = false; + this._var.container.querySelector('input[isall="1"]').checked = false; list = this.source.filter(it => it[valuekey] !== val); } else { list = this.selectedlist.filter(it => it[valuekey] !== val); } } - if (this.#allChecked) { - this.#label.innerText = r('allItem', '( All )'); + if (this._var.allChecked) { + this._var.label.innerText = r('allItem', '( All )'); } else { - selectItems(this.#label, list, htmlkey, textkey); + selectItems(this._var.label, list, htmlkey, textkey); } - this.#selectedList = list; + this._var.selectedList = list; if (typeof this.onselectedlist === 'function') { this.onselectedlist(itemlist); } diff --git a/lib/ui/extension.d.ts b/lib/ui/extension.d.ts new file mode 100644 index 0000000..2a8be64 --- /dev/null +++ b/lib/ui/extension.d.ts @@ -0,0 +1 @@ +export function validation(element: HTMLElement, regex: RegExp) : HTMLElement \ No newline at end of file diff --git a/lib/ui/extension.js b/lib/ui/extension.js new file mode 100644 index 0000000..3d41dcd --- /dev/null +++ b/lib/ui/extension.js @@ -0,0 +1,12 @@ +export function validation(element, regex) { + if (element instanceof HTMLElement && regex instanceof RegExp) { + element.addEventListener('change', e => { + if (regex.test(e.target.value)) { + e.target.classList.remove('validation-error'); + } else { + e.target.classList.add('validation-error'); + } + }) + } + return element; +} \ No newline at end of file diff --git a/lib/ui/grid/column.js b/lib/ui/grid/column.js index 193b282..fa91f37 100644 --- a/lib/ui/grid/column.js +++ b/lib/ui/grid/column.js @@ -102,12 +102,16 @@ const SymbolDropdown = Symbol.for('ui-dropdown'); export class GridDropdownColumn extends GridColumn { static createEdit(trigger, col, parent) { - const drop = new Dropdown({ ...col.dropOptions, parent }); + const drop = new Dropdown({ + ...col.dropOptions, + parent, + holder: parent + }); drop.onselected = trigger; return drop.create(); } - static #getDrop(element) { + static _getDrop(element) { const dropGlobal = global[SymbolDropdown]; if (dropGlobal == null) { return null; @@ -120,7 +124,7 @@ export class GridDropdownColumn extends GridColumn { return drop; } - static #getSource(item, col) { + static _getSource(item, col) { let source = col.source; if (typeof source === 'function') { source = source(item); @@ -128,7 +132,7 @@ export class GridDropdownColumn extends GridColumn { return source; } - static #setValue(source, element, val) { + static _setValue(source, element, val) { const data = source?.find(v => v.value === val); if (data != null) { val = data.text; @@ -138,20 +142,20 @@ export class GridDropdownColumn extends GridColumn { static setValue(element, val, item, col) { if (element.tagName !== 'DIV') { - let source = this.#getSource(item, col); + let source = this._getSource(item, col); if (source instanceof Promise) { - source.then(s => this.#setValue(s, element, val)); + source.then(s => this._setValue(s, element, val)); } else { - this.#setValue(source, element, val); + this._setValue(source, element, val); } return; } - const drop = this.#getDrop(element); + const drop = this._getDrop(element); if (drop == null) { return; } if (drop.source == null || drop.source.length === 0) { - let source = this.#getSource(item, col); + let source = this._getSource(item, col); if (source instanceof Promise) { source.then(s => { drop.source = s; @@ -171,7 +175,7 @@ export class GridDropdownColumn extends GridColumn { static setEnabled(element, enabled) { super.setEnabled(element , enabled); - const drop = this.#getDrop(element); + const drop = this._getDrop(element); if (drop == null) { return; } diff --git a/lib/ui/grid/grid.js b/lib/ui/grid/grid.js index aa301a9..8bf3dd8 100644 --- a/lib/ui/grid/grid.js +++ b/lib/ui/grid/grid.js @@ -1,6 +1,6 @@ import '../css/grid.scss'; import { global, isPositive, isMobile, throttle, truncate } from "../../utility"; -import { r } from "../../utility/lgres"; +import { r as lang } from "../../utility/lgres"; import { nullOrEmpty } from "../../utility/strings"; import { createElement } from "../../functions"; import { createIcon } from "../icon"; @@ -51,32 +51,37 @@ const ColumnTypes = { 5: GridTextColumn }; +let r = lang; + export class Grid { - #source; - #currentSource; - #parent; - #el; - #refs; - #rendering; - #selectedColumnIndex = -1; - #selectedIndexes; - #startIndex = 0; - #needResize; - #containerHeight; - #bodyClientWidth; - #rowCount = -1; - #scrollTop; - #scrollLeft; - #colTypes = {}; - #colAttrs = {}; - #vtable = []; + _var = { + selectedColumnIndex: -1, + startIndex: 0, + rowCount: -1, + colTypes: {}, + colAttrs: {} + }; + // _var.source; + // _var.currentSource; + // _var.parent; + // _var.el; + // _var.refs; + // _var.rendering; + // _var.selectedColumnIndex = -1; + // _var.selectedIndexes; + // _var.startIndex = 0; + // _var.needResize; + // _var.containerHeight; + // _var.bodyClientWidth; + // _var.rowCount = -1; + // _var.scrollTop; + // _var.scrollLeft; + // _var.colTypes = {}; + // _var.colAttrs = {}; + // _var.vtable = []; columns = []; - langs = { - all: r('allItem', '( All )'), - ok: r('ok', 'OK'), - reset: r('reset', 'Reset') - }; + langs = {}; virtualCount = 100; rowHeight = 36; lineHeight = 24; @@ -86,7 +91,6 @@ export class Grid { readonly; multiSelect = false; fullrowClick = true; - allowHtml = false; holderDisabled = false; headerVisible = true; window = global; @@ -110,32 +114,40 @@ export class Grid { isCheckbox(type) { return type === 3 } }; - constructor(container) { - this.#parent = container; + constructor(container, getText) { + this._var.parent = typeof container === 'string' ? document.querySelector(container) : container; + if (typeof getText === 'function') { + r = getText; + } + this.langs = { + all: r('allItem', '( All )'), + ok: r('ok', 'OK'), + reset: r('reset', 'Reset') + }; } - get element() { return this.#el } + get element() { return this._var.el } - get source() { return this.#source?.map(s => s.values) } + get source() { return this._var.source?.map(s => s.values) } set source(list) { - if (this.#el == null) { + if (this._var.el == null) { throw new Error('grid has not been initialized.') } if (!Array.isArray(list)) { throw new Error('source is not an Array.') } list = list.map(i => { return { values: i } }); - this.#source = list; - this.#refreshSource(list); + this._var.source = list; + this._refreshSource(list); } - #refreshSource(list) { - list ??= this.#source; - if (this.#colAttrs.__filtered === true) { - this.#currentSource = list.filter(it => { + _refreshSource(list) { + list ??= this._var.source; + if (this._var.colAttrs.__filtered === true) { + this._var.currentSource = list.filter(it => { for (let col of this.columns) { if (Array.isArray(col.filterValues)) { - const v = this.#getItemValue(it.values, col.key, col.filter); + const v = this._getItemValue(it.values, col.key, col.filter); if (col.filterValues.indexOf(v) < 0) { return false; } @@ -144,14 +156,14 @@ export class Grid { return true; }); } else { - this.#currentSource = list; + this._var.currentSource = list; } - this.#selectedColumnIndex = -1; - this.#selectedIndexes = []; - this.#startIndex = 0; - this.#scrollTop = 0; - this.#scrollLeft = 0; - this.#rowCount = -1; + this._var.selectedColumnIndex = -1; + this._var.selectedIndexes = []; + this._var.startIndex = 0; + this._var.scrollTop = 0; + this._var.scrollLeft = 0; + this._var.rowCount = -1; if (this.sortIndex >= 0) { this.sortColumn(); @@ -159,7 +171,7 @@ export class Grid { this.resize(); } - get virtual() { return this.#currentSource?.length > this.virtualCount } + get virtual() { return this._var.currentSource?.length > this.virtualCount } get sortKey() { if (this.columns == null) { @@ -168,15 +180,19 @@ export class Grid { return this.columns[this.sortIndex]?.key; } - get selectedIndexes() { return this.#selectedIndexes } + get tableRows() { + return [...this._var.refs.body.children]; //.filter(r => r.classList.contains('ui-grid-row')); + } + + get selectedIndexes() { return this._var.selectedIndexes } set selectedIndexes(indexes) { - const startIndex = this.#startIndex; - this.#selectedIndexes.splice(0, this.#selectedIndexes.length, ...indexes); + const startIndex = this._var.startIndex; + this._var.selectedIndexes.splice(0, this._var.selectedIndexes.length, ...indexes); if (this.readonly !== true) { this.refresh(); } else { - [...this.#refs.bodyContent.children].forEach((row, i) => { - if (indexes.indexOf(startIndex + i) >= 0) { + this.tableRows.forEach((row, i) => { + if (indexes.includes(startIndex + i)) { row.classList.add('selected'); } else if (row.classList.contains('selected')) { row.classList.remove('selected'); @@ -188,39 +204,39 @@ export class Grid { } } - get selectedIndex() { return (this.#selectedIndexes && this.#selectedIndexes[0]) ?? -1 } + get selectedIndex() { return (this._var.selectedIndexes && this._var.selectedIndexes[0]) ?? -1 } - get loading() { return this.#refs.loading?.style?.visibility === 'visible' } + get loading() { return this._var.refs.loading?.style?.visibility === 'visible' } set loading(flag) { - if (this.#refs.loading == null) { + if (this._var.refs.loading == null) { return; } if (flag === false) { - this.#refs.loading.style.visibility = 'hidden'; - this.#refs.loading.style.opacity = 0; + this._var.refs.loading.style.visibility = 'hidden'; + this._var.refs.loading.style.opacity = 0; } else { - this.#refs.loading.style.visibility = 'visible'; - this.#refs.loading.style.opacity = 1; + this._var.refs.loading.style.visibility = 'visible'; + this._var.refs.loading.style.opacity = 1; } } - get scrollTop() { return this.#refs.body?.scrollTop; } + get scrollTop() { return this._var.refs.el?.scrollTop; } set scrollTop(top) { - if (this.#refs.body == null) { + if (this._var.refs.el == null) { return; } - this.#refs.body.scrollTop = top; + this._var.refs.el.scrollTop = top; this.reload(); } - init(container = this.#parent) { - this.#el = null; - this.#refs = {}; - this.#rendering = true; + init(container = this._var.parent) { + this._var.el = null; + this._var.refs = {}; + this._var.rendering = true; if (!(container instanceof HTMLElement)) { throw new Error('no specified parent.'); } - this.#parent = container; + this._var.parent = container; const grid = createElement('div', 'ui-grid'); grid.setAttribute('tabindex', 0); grid.addEventListener('keydown', e => { @@ -234,14 +250,14 @@ export class Grid { } } else if (e.key === 'ArrowDown') { // down - const count = this.#currentSource?.length ?? 0; + const count = this._var.currentSource?.length ?? 0; if (index < count - 1) { flag = true; index += 1; } } if (flag) { - this.#selectedIndexes = [index]; + this._var.selectedIndexes = [index]; this.scrollToIndex(index); this.refresh(); if (typeof this.selectedRowChanged === 'function') { @@ -253,85 +269,94 @@ export class Grid { container.replaceChildren(grid); const sizer = createElement('span', 'ui-grid-sizer'); grid.appendChild(sizer); - this.#refs.sizer = sizer; + this._var.refs.sizer = sizer; + + grid.addEventListener('scroll', e => throttle(this._onScroll, RefreshInterval, this, e), { passive: true }); // header & body - const header = this.#createHeader(); - grid.appendChild(header); - const body = this.#createBody(); - grid.appendChild(body); + const wrapper = createElement('div', 'ui-grid-wrapper'); + this._var.refs.wrapper = wrapper; + grid.appendChild(wrapper); + const table = createElement('table', 'ui-grid-table'); + this._var.refs.table = table; + this._createHeader(table); + this._createBody(table); + wrapper.appendChild(table); // loading const loading = createElement('div', 'ui-grid-loading', createElement('div', null, createIcon('fa-regular', 'spinner-third')) ); - this.#refs.loading = loading; + this._var.refs.loading = loading; grid.appendChild(loading); - this.#el = grid; + this._var.el = grid; - this.#rendering = false; - if (this.#source != null && this.sortIndex >= 0) { + this._var.rendering = false; + if (this._var.source != null && this.sortIndex >= 0) { this.sortColumn(); } } + setData(source) { + this.source = source; + } + scrollToIndex(index) { - const top = this.#scrollToTop(index * (this.rowHeight + 1), true); - this.#refs.body.scrollTop = top; + const top = this._scrollToTop(index * (this.rowHeight + 1), true); + this._var.refs.el.scrollTop = top; } resize(force) { - if (this.#rendering || this.#el == null) { + if (this._var.rendering || this._var.el == null) { return; } - const body = this.#refs.body; - // let height = this.#refs.header.offsetHeight + 2; + const body = this._var.refs.body; + // let height = this._var.refs.header.offsetHeight + 2; // let top = body.offsetTop; // if (top !== height) { // body.style.top = `${height}px`; // top = height; // } - const top = this.headerVisible === false ? 0 : this.#refs.header.offsetHeight; + const top = this.headerVisible === false ? 0 : this._var.refs.header.offsetHeight; let height = this.height; if (height === 0) { - height = this.#containerHeight; + height = this._var.containerHeight; } else if (isNaN(height) || height < 0) { - height = this.#el.offsetHeight - top; + height = this._var.el.offsetHeight - top; } const count = truncate((height - 1) / (this.rowHeight + 1)) + (RedumCount * 2) + 1; - if (force || count !== this.#rowCount) { - this.#rowCount = count; + if (force || count !== this._var.rowCount) { + this._var.rowCount = count; this.reload(); } - this.#bodyClientWidth = body.clientWidth; + this._var.bodyClientWidth = body.clientWidth; } reload() { - let length = this.#currentSource.length; + let length = this._var.currentSource.length; if (this.extraRows > 0) { length += this.extraRows; } - this.#containerHeight = length * (this.rowHeight + 1); - this.#refs.body.scrollTop = 0; - this.#refs.body.scrollLeft = 0; - this.#refs.bodyContent.style.top = '0px'; - this.#refs.bodyContainer.style.height = `${this.#containerHeight}px`; - this.#adjustRows(this.#refs.bodyContent); + this._var.containerHeight = length * (this.rowHeight + 1); + // this._var.refs.body.scrollTop = 0; + // this._var.refs.body.scrollLeft = 0; + this._var.refs.table.style.top = '0px'; + this._var.refs.wrapper.style.height = `${this._var.containerHeight}px`; + this._adjustRows(this._var.refs.body); this.refresh(); } refresh() { - if (this.#refs.bodyContent == null) { + if (this._var.refs.body == null) { throw new Error('body has not been created.'); } - const rows = this.#refs.bodyContent.children; const widths = {}; - this.#fillRows(rows, this.columns, widths); - if (this.#needResize && widths.flag) { - this.#needResize = false; + this._fillRows(this.tableRows, this.columns, widths); + if (this._var.needResize && widths.flag) { + this._var.needResize = false; this.columns.forEach((col, i) => { - if (!this.#get(col.key, 'autoResize')) { + if (!this._get(col.key, 'autoResize')) { return; } let width = widths[i]; @@ -339,17 +364,17 @@ export class Grid { width = col.width; } if (width > 0) { - this.#changeColumnWidth(i, width); + this._changeColumnWidth(i, width); } }); } } resetChange() { - if (this.#source == null) { + if (this._var.source == null) { return; } - for (let row of this.#source) { + for (let row of this._var.source) { delete row.__changed; } } @@ -361,7 +386,7 @@ export class Grid { return; } const direction = this.sortDirection; - [...this.#refs.header.children].forEach((th, i) => { + [...this._var.refs.header.children].forEach((th, i) => { const arrow = th.querySelector('.arrow'); if (arrow == null) { return; @@ -374,13 +399,13 @@ export class Grid { }); let comparer; if (typeof col.sortFilter !== 'function') { - const direction = this.sortDirection; + let direction = this.sortDirection; if (isNaN(direction)) { direction = 1; } comparer = (a, b) => { - a = this.#getItemValue(a.values, col.key, col.filter); - b = this.#getItemValue(b.values, col.key, col.filter); + a = this._getItemValue(a.values, col.key, col.filter); + b = this._getItemValue(b.values, col.key, col.filter); if (a == null && typeof b === 'number') { a = 0; } else if (typeof a === 'number' && b == null) { @@ -396,11 +421,11 @@ export class Grid { } else { comparer = (a, b) => col.sortFilter(a.values, b.values) * direction; } - this.#source.sort(comparer); - if (this.#colAttrs.__filtered === true) { - this.#currentSource.sort(comparer); + this._var.source.sort(comparer); + if (this._var.colAttrs.__filtered === true) { + this._var.currentSource.sort(comparer); } - if (this.#rowCount < 0) { + if (this._var.rowCount < 0) { return; } if (reload) { @@ -411,25 +436,27 @@ export class Grid { } clearHeaderCheckbox() { - const boxes = this.#refs.header.querySelectorAll('.ui-check-wrapper>input'); + const boxes = this._var.refs.header.querySelectorAll('.ui-check-wrapper>input'); boxes.forEach(box => box.checked = false); } - #createHeader() { - const thead = createElement('table', 'ui-grid-header'); + _createHeader(table) { + const thead = createElement('thead'); if (this.headerVisible === false) { thead.style.display = 'none'; } + table.appendChild(thead); const header = createElement('tr'); thead.appendChild(header); - const sizer = this.#refs.sizer; + const sizer = this._var.refs.sizer; + let left = 0; for (let col of this.columns) { if (col.visible === false) { const hidden = createElement('th'); hidden.style.display = 'none'; if (col.sortable !== false) { hidden.dataset.key = col.key; - hidden.addEventListener('click', e => this.#onHeaderClicked(e, col, true)); + hidden.addEventListener('click', e => this._onHeaderClicked(e, col, true)); } header.appendChild(hidden); continue; @@ -439,8 +466,8 @@ export class Grid { if (col.width > 0) { // col.autoResize = false; } else { - this.#set(col.key, 'autoResize', true); - this.#needResize = true; + this._set(col.key, 'autoResize', true); + this._var.needResize = true; sizer.innerText = col.caption ?? ''; let width = sizer.offsetWidth + 22; if (!this.readonly && col.enabled !== false && col.allcheck && isCheckbox) { @@ -465,26 +492,31 @@ export class Grid { 'min-width': w, 'text-align': col.align }; - this.#set(col.key, 'style', style); + this._set(col.key, 'style', style); // element const th = createElement('th', 'column'); + if (col.isfixed) { + th.classList.add('sticky'); + th.style.left = `${left}px`; + } + left += col.width; th.dataset.key = col.key; for (let css of Object.entries(style)) { th.style.setProperty(css[0], css[1]); } if (col.sortable) { th.style.cursor = 'pointer'; - th.addEventListener('click', e => this.#onHeaderClicked(e, col)); + th.addEventListener('click', e => this._onHeaderClicked(e, col)); } if (col.orderable !== false) { col.orderable = true; - th.addEventListener('mousedown', e => this.#onDragStart(e, col)); + th.addEventListener('mousedown', e => this._onDragStart(e, col)); } const wrapper = createElement('div'); th.appendChild(wrapper); if (!this.readonly && col.enabled !== false && col.allcheck && isCheckbox) { const check = createCheckbox({ - onchange: e => this.#onColumnAllChecked(col, e.target.checked) + onchange: e => this._onColumnAllChecked(col, e.target.checked) }); wrapper.appendChild(check); } @@ -504,15 +536,15 @@ export class Grid { if (col.allowFilter === true) { const filter = createElement('layer', 'filter'); filter.appendChild(createIcon('fa-solid', 'filter')); - filter.addEventListener('mousedown', e => this.#onFilter(e, col)); + filter.addEventListener('mousedown', e => this._onFilter(e, col)); th.classList.add('header-filter'); th.appendChild(filter); } // resize spliter if (col.resizable !== false) { const spliter = createElement('layer', 'spliter'); - spliter.addEventListener('mousedown', e => this.#onResizeStart(e, col)); - spliter.addEventListener('dblclick', e => this.#onAutoResize(e, col)); + spliter.addEventListener('mousedown', e => this._onResizeStart(e, col)); + spliter.addEventListener('dblclick', e => this._onAutoResize(e, col)); th.appendChild(spliter); } // tooltip @@ -524,15 +556,15 @@ export class Grid { header.appendChild(createElement('th', null, dragger, draggerCursor)); sizer.replaceChildren(); - this.#refs.header = header; - this.#refs.dragger = dragger; - this.#refs.draggerCursor = draggerCursor; + this._var.refs.header = header; + this._var.refs.dragger = dragger; + this._var.refs.draggerCursor = draggerCursor; return thead; } - #createBody() { - const body = createElement('div', 'ui-grid-body'); - body.addEventListener('scroll', e => throttle(this.#onScroll, RefreshInterval, this, e), { passive: true }); + _createBody(table) { + const body = createElement('tbody'); + table.appendChild(body); const cols = this.columns; let width = 1; for (let col of cols) { @@ -540,73 +572,79 @@ export class Grid { width += col.width + 1; } } - // body container - const bodyContainer = createElement('div'); - bodyContainer.style.position = 'relative'; - bodyContainer.style.minWidth = '100%'; - bodyContainer.style.minHeight = '1px'; + // // body container + // const bodyContainer = createElement('div'); + // bodyContainer.style.position = 'relative'; + // bodyContainer.style.minWidth = '100%'; + // bodyContainer.style.minHeight = '1px'; + // if (width > 0) { + // bodyContainer.style.width = `${width}px`; + // } + // body.appendChild(bodyContainer); if (width > 0) { - bodyContainer.style.width = `${width}px`; + table.style.width = `${width}px`; } - body.appendChild(bodyContainer); // body content - const bodyContent = createElement('table', 'ui-grid-body-content'); - bodyContent.addEventListener('mousedown', e => { - let [parent, target] = this.#getRowTarget(e.target); + body.addEventListener('mousedown', e => { + let [parent, target] = this._getRowTarget(e.target); const rowIndex = indexOfParent(parent); let colIndex = indexOfParent(target); if (colIndex >= this.columns.length) { colIndex = -1; } - this.#onRowClicked(e, rowIndex, colIndex); + this._onRowClicked(e, rowIndex, colIndex); }); - bodyContent.addEventListener('dblclick', e => this.#onRowDblClicked(e)); - bodyContainer.appendChild(bodyContent); - // this.#adjustRows(); + body.addEventListener('dblclick', e => this._onRowDblClicked(e)); + // this._adjustRows(); // events - if (!this.holderDisabled) { - const holder = createElement('div', 'ui-grid-hover-holder'); - holder.addEventListener('mousedown', e => { - const holder = e.currentTarget; - const row = Number(holder.dataset.row); - const col = Number(holder.dataset.col); - if (holder.classList.contains('active')) { - holder.classList.remove('active'); - } - return this.#onRowClicked(e, row + this.#startIndex, col); - }); - holder.addEventListener('dblclick', e => this.#onRowDblClicked(e)); - bodyContainer.appendChild(holder); - body.addEventListener('mousemove', e => throttle(this.#onBodyMouseMove, HoverInternal, this, e, holder), { passive: true }); - } - this.#refs.body = body; - this.#refs.bodyContainer = bodyContainer; - this.#refs.bodyContent = bodyContent; + // if (!this.holderDisabled) { + // const holder = createElement('div', 'ui-grid-hover-holder'); + // holder.addEventListener('mousedown', e => { + // const holder = e.currentTarget; + // const row = Number(holder.dataset.row); + // const col = Number(holder.dataset.col); + // if (holder.classList.contains('active')) { + // holder.classList.remove('active'); + // } + // return this._onRowClicked(e, row + this._var.startIndex, col); + // }); + // holder.addEventListener('dblclick', e => this._onRowDblClicked(e)); + // bodyContainer.appendChild(holder); + // body.addEventListener('mousemove', e => throttle(this._onBodyMouseMove, HoverInternal, this, e, holder), { passive: true }); + // } + this._var.refs.body = body; + // this._var.refs.bodyContainer = bodyContainer; // this.refresh(); return body; } - #adjustRows() { - let count = this.#rowCount; + _adjustRows() { + let count = this._var.rowCount; if (isNaN(count) || count < 0 || !this.virtual) { - count = this.#currentSource.length; + count = this._var.currentSource.length; } const cols = this.columns; - const content = this.#refs.bodyContent; + const content = this._var.refs.body; const exists = content.children.length; count -= exists; if (count > 0) { for (let i = 0; i < count; i += 1) { const row = createElement('tr', 'ui-grid-row'); - // row.addEventListener('mousedown', e => this.#onRowClicked(e, exists + i)); - // row.addEventListener('dblclick', e => this.#onRowDblClicked(e)); + // row.addEventListener('mousedown', e => this._onRowClicked(e, exists + i)); + // row.addEventListener('dblclick', e => this._onRowDblClicked(e)); + let left = 0; cols.forEach((col, j) => { const cell = createElement('td'); if (col.visible !== false) { + if (col.isfixed) { + cell.classList.add('sticky'); + cell.style.left = `${left}px`; + } + left += col.width; cell.dataset.row = String(exists + i); cell.dataset.col = String(j); - const style = this.#get(col.key, 'style'); + const style = this._get(col.key, 'style'); if (style != null) { for (let css of Object.entries(style)) { cell.style.setProperty(css[0], css[1]); @@ -618,42 +656,42 @@ export class Grid { } } if (Grid.ColumnTypes.isCheckbox(col.type)) { - cell.appendChild(GridCheckboxColumn.createEdit(e => this.#onRowChanged(e, exists + i, col, e.target.checked, cell))); - // this.#colTypes[col.key] = GridCheckboxColumn; + cell.appendChild(GridCheckboxColumn.createEdit(e => this._onRowChanged(e, exists + i, col, e.target.checked, cell))); + // this._var.colTypes[col.key] = GridCheckboxColumn; } else { - let type = this.#colTypes[col.key]; + let type = this._var.colTypes[col.key]; if (type == null) { if (isNaN(col.type)) { - if (this.allowHtml && col.type != null) { - type = col.type; - } + type = col.type; } else { type = ColumnTypes[col.type]; } type ??= GridColumn; - this.#colTypes[col.key] = type; + this._var.colTypes[col.key] = type; } - cell.appendChild(type.create(col)); + cell.appendChild(type.create(col, e => this._onRowChanged(e, this._var.startIndex + i, col, type.getValue(e), cell), this._var.refs.body)); } + } else { + cell.style.display = 'none'; } row.appendChild(cell); }); - row.appendChild(createElement('td')); + row.appendChild(createElement('td', td => td.innerText = '\u00a0')); content.appendChild(row); } } else if (count < 0) { for (let i = -1; i >= count; i -= 1) { // content.removeChild(content.children[exists + i]); - content.children[exists + i].remove(); + content.children[exists + i + 1].remove(); } } } - #fillRows(rows, cols, widths) { - const startIndex = this.#startIndex; - const selectedIndexes = this.#selectedIndexes; + _fillRows(rows, cols, widths) { + const startIndex = this._var.startIndex; + const selectedIndexes = this._var.selectedIndexes; [...rows].forEach((row, i) => { - const vals = this.#currentSource[startIndex + i]; + const vals = this._var.currentSource[startIndex + i]; if (vals == null) { return; } @@ -661,7 +699,7 @@ export class Grid { return; } const item = vals.values; - const selected = selectedIndexes.indexOf(startIndex + i) >= 0; + const selected = selectedIndexes.includes(startIndex + i); if (selected) { row.classList.add('selected'); } else if (row.classList.contains('selected')) { @@ -682,7 +720,7 @@ export class Grid { if (col.text != null) { val = col.text; } else if (typeof col.filter === 'function') { - val = col.filter(item); + val = col.filter(item, this._var.refs.body); } else { val = item[col.key]; if (val?.displayValue != null) { @@ -697,15 +735,15 @@ export class Grid { cell.style.backgroundColor = bgColor ?? ''; } const isCheckbox = Grid.ColumnTypes.isCheckbox(col.type); - const type = isCheckbox ? GridCheckboxColumn : this.#colTypes[col.key] ?? GridColumn; + const type = isCheckbox ? GridCheckboxColumn : this._var.colTypes[col.key] ?? GridColumn; let element; if (!isCheckbox && selectChanged && typeof type.createEdit === 'function') { if (vals.__editing?.[col.key] && type.editing) { val = type.getValue({ target: cell.children[0] }); - this.#onRowChanged(null, startIndex + i, col, val, cell, true); + this._onRowChanged(null, startIndex + i, col, val, cell, true); } element = selected ? - type.createEdit(e => this.#onRowChanged(e, startIndex + i, col, type.getValue(e), cell), col, this.#refs.bodyContent, vals) : + type.createEdit(e => this._onRowChanged(e, startIndex + i, col, type.getValue(e), cell), col, this._var.refs.body, vals) : type.create(col); cell.replaceChildren(element); } else { @@ -736,7 +774,7 @@ export class Grid { type.setEnabled(element, enabled); } // auto resize - if (this.#needResize && this.#get(col.key, 'autoResize')) { + if (this._var.needResize && this._get(col.key, 'autoResize')) { const width = element.scrollWidth + 12; if (width > 0 && widths != null && (isNaN(widths[j]) || widths[j] < width)) { widths[j] = width; @@ -770,47 +808,72 @@ export class Grid { }); } - #changeColumnWidth(index, width) { + _changeColumnWidth(index, width) { const col = this.columns[index]; // const oldwidth = col.width; const w = `${width}px`; col.width = width; - const style = this.#get(col.key, 'style'); + const style = this._get(col.key, 'style'); style.width = w; style['max-width'] = w; style['min-width'] = w; - let element = this.#refs.header.children[index]; + const headerCells = this._var.refs.header.children; + let element = headerCells[index]; element.style.width = w; element.style.maxWidth = w; element.style.minWidth = w; - const body = this.#refs.bodyContent; - for (let row of body.children) { + let left = 0; + if (col.isfixed) { + left = element.offsetLeft + width; + let l = left; + for (let i = index + 1; i < this.columns.length; ++i) { + if (this.columns[i].isfixed) { + headerCells[i].style.left = `${l}px`; + l += this.columns[i].width; + } else { + break; + } + } + } + for (let row of this.tableRows) { element = row.children[index]; if (element != null) { element.style.width = w; element.style.maxWidth = w; element.style.minWidth = w; + if (col.isfixed) { + let l = left; + for (let i = index + 1; i < this.columns.length; ++i) { + if (this.columns[i].isfixed) { + row.children[i].style.left = `${l}px`; + l += this.columns[i].width; + } else { + break; + } + } + } } } // } else { - // width = this.#refs.bodyContainer.offsetWidth - oldwidth + width; - // this.#refs.bodyContainer.style.width = `${width}px`; + // width = this._var.refs.bodyContainer.offsetWidth - oldwidth + width; + // this._var.refs.bodyContainer.style.width = `${width}px`; // } } - #changingColumnOrder(index, offset, x, offsetLeft) { - const children = this.#refs.header.children; + _changingColumnOrder(index, offset, x, offsetLeft) { + const children = this._var.refs.header.children; let element = children[index]; - this.#refs.dragger.style.left = `${element.offsetLeft - offsetLeft + offset}px`; - this.#refs.dragger.style.width = element.style.width; - this.#refs.dragger.style.display = 'block'; - offset = x - getOffsetLeftFromWindow(element); + this._var.refs.dragger.style.left = `${element.offsetLeft - offsetLeft + offset}px`; + this._var.refs.dragger.style.width = element.style.width; + this._var.refs.dragger.style.display = 'block'; + offset = x - element.offsetLeft; // getOffsetLeftFromWindow(element); let idx; if (offset < 0) { offset = -offset; for (let i = index - 1; i >= 0 && offset >= 0; i -= 1) { element = children[i]; - if (element == null || element.className !== 'column') { + if (element == null || !element.className || element.classList.contains('sticky')) { + idx = i + 1; break; } if (offset < element.offsetWidth) { @@ -824,7 +887,7 @@ export class Grid { const count = children.length; for (let i = index; i < count - 1 && offset >= 0; i += 1) { element = children[i]; - if (element == null || element.className !== 'column') { + if (element == null || !element.className || element.classList.contains('sticky')) { idx = i; break; } @@ -836,29 +899,29 @@ export class Grid { } idx ??= count - 1; } - if (idx !== this.#colAttrs.__orderIndex) { - this.#colAttrs.__orderIndex = idx; + if (idx !== this._var.colAttrs.__orderIndex) { + this._var.colAttrs.__orderIndex = idx; element = children[idx]; if (element == null) { return; } - this.#refs.draggerCursor.style.left = `${element.offsetLeft - offsetLeft}px`; - this.#refs.draggerCursor.style.display = 'block'; + this._var.refs.draggerCursor.style.left = `${element.offsetLeft - offsetLeft}px`; + this._var.refs.draggerCursor.style.display = 'block'; } } - #changeColumnOrder(index) { - this.#refs.dragger.style.display = ''; - this.#refs.draggerCursor.style.display = ''; - const orderIndex = this.#colAttrs.__orderIndex; + _changeColumnOrder(index) { + this._var.refs.dragger.style.display = ''; + this._var.refs.draggerCursor.style.display = ''; + const orderIndex = this._var.colAttrs.__orderIndex; if (orderIndex >= 0 && orderIndex !== index) { let targetIndex = orderIndex - index; if (targetIndex >= 0 && targetIndex <= 1) { return; } - const header = this.#refs.header; + const header = this._var.refs.header; const children = header.children; - const rows = this.#refs.bodyContent.children; + const rows = this.tableRows; const columns = this.columns; if (targetIndex > 1) { targetIndex = orderIndex - 1; @@ -904,13 +967,13 @@ export class Grid { } } - #scrollToTop(top, reload) { + _scrollToTop(top, reload) { const rowHeight = (this.rowHeight + 1); top -= (top % (rowHeight * 2)) + (RedumCount * rowHeight); if (top < 0) { top = 0; } else { - let bottomTop = this.#containerHeight - (reload ? 0 : this.#rowCount * rowHeight); + let bottomTop = this._var.containerHeight - (reload ? 0 : this._var.rowCount * rowHeight); if (bottomTop < 0) { bottomTop = 0; } @@ -918,14 +981,14 @@ export class Grid { top = bottomTop; } } - if (this.#scrollTop !== top) { - this.#scrollTop = top; + if (this._var.scrollTop !== top) { + this._var.scrollTop = top; if (this.virtual) { - this.#startIndex = top / rowHeight; + this._var.startIndex = top / rowHeight; } this.refresh(); if (this.virtual) { - this.#refs.bodyContent.style.top = `${top}px`; + this._var.refs.table.style.top = `${top}px`; } } else if (reload) { this.refresh(); @@ -934,34 +997,34 @@ export class Grid { return top; } - #get(key, name) { - const attr = this.#colAttrs[key]; + _get(key, name) { + const attr = this._var.colAttrs[key]; if (attr == null) { return null; } return attr[name]; } - #set(key, name, value) { - const attr = this.#colAttrs[key]; + _set(key, name, value) { + const attr = this._var.colAttrs[key]; if (attr == null) { - this.#colAttrs[key] = { [name]: value }; + this._var.colAttrs[key] = { [name]: value }; } else { attr[name] = value; } } - #getItemValue(item, key, filter) { + _getItemValue(item, key, filter) { let value; if (typeof filter === 'function') { - value = filter(item); + value = filter(item, this._var.refs.body); } else { value = item[key]; } return value?.value ?? value; } - #getRowTarget(target) { + _getRowTarget(target) { let parent; while ((parent = target.parentElement) != null && !parent.classList.contains('ui-grid-row')) { target = parent; @@ -969,15 +1032,15 @@ export class Grid { return [parent, target]; } - #notHeader(tagName) { + _notHeader(tagName) { return /^(input|label|layer|svg|use)$/i.test(tagName); } - #onHeaderClicked(e, col, force) { - if (!force && (this.#get(col.key, 'resizing') || this.#get(col.key, 'dragging'))) { + _onHeaderClicked(e, col, force) { + if (!force && (this._get(col.key, 'resizing') || this._get(col.key, 'dragging'))) { return; } - if (!this.#notHeader(e.target.tagName)) { + if (!this._notHeader(e.target.tagName)) { const index = this.columns.indexOf(col); if (index < 0) { return; @@ -994,41 +1057,39 @@ export class Grid { } } - #onCloseFilter() { - const panels = this.#el.querySelectorAll('.filter-panel.active'); + _onCloseFilter(e) { + if (e != null) { + if ((e.target.tagName === 'LAYER' && e.target.classList.contains('filter')) || + e.target.tagName === 'use') { + return false; + } + } + const panels = this._var.el.querySelectorAll('.filter-panel.active'); if (panels.length > 0) { panels.forEach(el => el.classList.remove('active')); - setTimeout(() => this.#el.querySelectorAll('.filter-panel').forEach(el => el.remove()), 120); - const filtering = this.#colAttrs.__filtering; + setTimeout(() => this._var.el.querySelectorAll('.filter-panel').forEach(el => el.remove()), 120); + const filtering = this._var.colAttrs.__filtering; if (filtering instanceof HTMLElement) { filtering.classList.remove('hover'); } - delete this.#colAttrs.__filtering; + delete this._var.colAttrs.__filtering; + document.removeEventListener('mousedown', this._onCloseFilter); return true; } return false; } - #onFilter(e, col) { - if (this.#onCloseFilter()) { + _onFilter(e, col) { + if (this._onCloseFilter()) { return; } - const close = e => { - if ((e.target.tagName === 'LAYER' && e.target.classList.contains('filter')) || - e.target.tagName === 'use') { - return; - } - if (this.#onCloseFilter()) { - document.removeEventListener('mousedown', close); - } - } - document.addEventListener('mousedown', close); + document.addEventListener('mousedown', this._onCloseFilter.bind(this)); const panel = createElement('div', 'filter-panel'); panel.addEventListener('mousedown', e => e.stopPropagation()); const filter = e.currentTarget; const th = filter.parentElement; const width = th.offsetWidth; - panel.style.top = `${th.offsetHeight}px`; + panel.style.top = `${th.offsetHeight + this._var.el.scrollTop}px`; panel.style.left = (th.offsetLeft + (width > FilterPanelWidth ? width - FilterPanelWidth : 0)) + 'px'; // search @@ -1047,7 +1108,7 @@ export class Grid { } // list const itemlist = createElement('div', 'filter-item-list'); - itemlist.addEventListener('scroll', e => throttle(this.#onFilterScroll, RefreshInterval, this, col, itemlist, e.target.scrollTop), { passive: true }); + itemlist.addEventListener('scroll', e => throttle(this._onFilterScroll, RefreshInterval, this, col, itemlist, e.target.scrollTop), { passive: true }); // - all const itemall = createElement('div', 'filter-item filter-all'); itemall.appendChild(createCheckbox({ @@ -1066,8 +1127,8 @@ export class Grid { array = col.filterSource.call(this, col); } else { const dict = Object.create(null); - for (let item of this.#source) { - const val = this.#getItemValue(item.values, col.key, col.filter); + for (let item of this._var.source) { + const val = this._getItemValue(item.values, col.key, col.filter); if (!Object.hasOwnProperty.call(dict, val)) { const v = item.values[col.key]; dict[val] = { @@ -1093,7 +1154,7 @@ export class Grid { displayValue: i == null ? '' : i }; }); - this.#fillFilterList(col, itemlist, array, itemall); + this._fillFilterList(col, itemlist, array, itemall); itemall.querySelector('input').checked = ![...itemlist.querySelectorAll('.filter-content input')].some(i => !i.checked); panel.appendChild(itemlist); if (searchbox != null) { @@ -1101,9 +1162,9 @@ export class Grid { const key = e.currentTarget.value.toLowerCase(); const items = key.length === 0 ? array : array.filter(i => { const displayValue = i?.displayValue ?? i; - return String(displayValue ?? '').indexOf(key) >= 0; + return String(displayValue ?? '').includes(key); }); - this.#fillFilterList(col, itemlist, items, itemall); + this._fillFilterList(col, itemlist, items, itemall); }); } // function @@ -1112,65 +1173,65 @@ export class Grid { createElement('button', ok => { ok.innerText = this.langs.ok; ok.addEventListener('click', () => { - const array = this.#get(col.key, 'filterSource').filter(i => i.__checked !== false); + const array = this._get(col.key, 'filterSource').filter(i => i.__checked !== false); if (typeof col.onFilterOk === 'function') { col.onFilterOk.call(this, col, array); } else { col.filterValues = array.map(a => a.value); } - this.#colAttrs.__filtered = true; - this.#refreshSource(); + this._var.colAttrs.__filtered = true; + this._refreshSource(); if (typeof col.onFiltered === 'function') { col.onFiltered.call(this, col); } filter.classList.add('active'); - this.#onCloseFilter(); + this._onCloseFilter(); }); }), createElement('button', reset => { reset.innerText = this.langs.reset; reset.addEventListener('click', () => { delete col.filterValues; - this.#colAttrs.__filtered = this.columns.some(c => col.filterValues != null) - this.#refreshSource(); + this._var.colAttrs.__filtered = this.columns.some(c => col.filterValues != null) + this._refreshSource(); if (typeof col.onFiltered === 'function') { col.onFiltered.call(this, col); } filter.classList.remove('active'); - this.#onCloseFilter(); + this._onCloseFilter(); }); }) ); panel.appendChild(functions); - this.#el.appendChild(panel); + this._var.el.appendChild(panel); setTimeout(() => panel.classList.add('active'), 0); - this.#colAttrs.__filtering = filter; + this._var.colAttrs.__filtering = filter; filter.classList.add('hover'); } - #fillFilterList(col, list, array, all) { + _fillFilterList(col, list, array, all) { list.querySelector('.filter-holder')?.remove(); list.querySelector('.filter-content')?.remove(); const rowHeight = this.filterRowHeight; const height = array.length * rowHeight; - this.#set(col.key, 'filterHeight', height); + this._set(col.key, 'filterHeight', height); const holder = createElement('div', 'filter-holder'); holder.style.height = `${height}px`; const content = createElement('div', 'filter-content'); content.style.top = `${rowHeight}px`; - this.#set(col.key, 'filterSource', array); + this._set(col.key, 'filterSource', array); for (let item of array) { - item.__checked = !Array.isArray(col.filterValues) || col.filterValues.indexOf(item.value ?? item) >= 0; + item.__checked = !Array.isArray(col.filterValues) || col.filterValues.includes(item.value ?? item); } if (array.length > 12) { array = array.slice(0, 12); } - this.#doFillFilterList(content, array, all); + this._doFillFilterList(content, array, all); list.append(holder, content); } - #doFillFilterList(content, array, all) { + _doFillFilterList(content, array, all) { for (let item of array) { const div = createElement('div', 'filter-item'); div.appendChild(createCheckbox({ @@ -1185,13 +1246,13 @@ export class Grid { } } - #onFilterScroll(col, list, top) { + _onFilterScroll(col, list, top) { const rowHeight = this.filterRowHeight; top -= (top % (rowHeight * 2)) + rowHeight; if (top < 0) { top = 0; } else { - let bottomTop = this.#get(col.key, 'filterHeight') - (12 * rowHeight); + let bottomTop = this._get(col.key, 'filterHeight') - (12 * rowHeight); if (bottomTop < 0) { bottomTop = 0; } @@ -1199,10 +1260,10 @@ export class Grid { top = bottomTop; } } - if (this.#get(col.key, 'filterTop') !== top) { - this.#set(col.key, 'filterTop', top); + if (this._get(col.key, 'filterTop') !== top) { + this._set(col.key, 'filterTop', top); const startIndex = top / rowHeight; - let array = this.#get(col.key, 'filterSource'); + let array = this._get(col.key, 'filterSource'); if (startIndex + 12 < array.length) { array = array.slice(startIndex, startIndex + 12); } else { @@ -1210,17 +1271,20 @@ export class Grid { } const content = list.querySelector('.filter-content'); content.replaceChildren(); - this.#doFillFilterList(content, array, list.querySelector('.filter-all>input')); + this._doFillFilterList(content, array, list.querySelector('.filter-all>input')); content.style.top = `${top + rowHeight}px`; } } - #onDragStart(e, col) { - if (this.#notHeader(e.target.tagName)) { + _onDragStart(e, col) { + if (this._notHeader(e.target.tagName)) { + return; + } + if (e.currentTarget.classList.contains('sticky')) { return; } - const cx = getClientX(e); const index = indexOfParent(e.currentTarget); + const cx = getClientX(e); const clearEvents = attr => { for (let event of ['mousemove', 'mouseup']) { if (attr.hasOwnProperty(event)) { @@ -1229,14 +1293,14 @@ export class Grid { } } }; - let attr = this.#colAttrs[col.key]; + let attr = this._var.colAttrs[col.key]; if (attr == null) { - attr = this.#colAttrs[col.key] = {}; + attr = this._var.colAttrs[col.key] = {}; } else { clearEvents(attr); } attr.dragging = true; - const offsetLeft = this.#refs.header.querySelector('th:last-child').offsetLeft; + const offsetLeft = this._var.refs.header.querySelector('th:last-child').offsetLeft; const dragmove = e => { const cx2 = getClientX(e); const offset = cx2 - cx; @@ -1248,7 +1312,7 @@ export class Grid { dragging = true; } if (dragging) { - this.#changingColumnOrder(index, offset, cx2, offsetLeft); + this._changingColumnOrder(index, offset, cx2, offsetLeft); attr.offset = offset; } }; @@ -1262,13 +1326,13 @@ export class Grid { delete attr.dragging; delete attr.offset; }); - this.#changeColumnOrder(index); + this._changeColumnOrder(index); } }; ['mousemove', 'mouseup'].forEach(event => window.addEventListener(event, attr[event])); } - #onResizeStart(e, col) { + _onResizeStart(e, col) { const cx = getClientX(e); const width = col.width; const index = indexOfParent(e.currentTarget.parentElement); @@ -1281,9 +1345,9 @@ export class Grid { } } }; - let attr = this.#colAttrs[col.key]; + let attr = this._var.colAttrs[col.key]; if (attr == null) { - attr = this.#colAttrs[col.key] = {}; + attr = this._var.colAttrs[col.key] = {}; } else { clearEvents(attr); } @@ -1296,7 +1360,7 @@ export class Grid { } attr.resizing = val; attr.sizing = true; - this.#changeColumnWidth(index, val); + this._changeColumnWidth(index, val); }; attr.mousemove = e => throttle(resizemove, RefreshInterval, this, e); attr.mouseup = e => { @@ -1307,7 +1371,7 @@ export class Grid { if (attr.sizing) { delete attr.sizing; delete attr.autoResize; - this.#changeColumnWidth(index, width); + this._changeColumnWidth(index, width); if (typeof this.columnChanged === 'function') { this.columnChanged(ColumnChangedType.Resize, index, width); } @@ -1319,11 +1383,11 @@ export class Grid { ['mousemove', 'mouseup'].forEach(event => window.addEventListener(event, attr[event])); } - #onAutoResize(e, col) { + _onAutoResize(e, col) { const th = e.currentTarget.parentElement; const index = indexOfParent(th); let width = th.querySelector('div:first-child').scrollWidth; - for (let row of this.#refs.bodyContent.children) { + for (let row of this.tableRows) { const element = row.children[index].children[0]; const w = element.scrollWidth; if (w > width) { @@ -1335,15 +1399,15 @@ export class Grid { } if (width > 0 && width !== col.width) { width += 12; - this.#changeColumnWidth(index, width); + this._changeColumnWidth(index, width); if (typeof this.columnChanged === 'function') { this.columnChanged(ColumnChangedType.Resize, index, width); } } } - #onColumnAllChecked(col, flag) { - if (this.#currentSource == null) { + _onColumnAllChecked(col, flag) { + if (this._var.currentSource == null) { return; } const key = col.key; @@ -1352,7 +1416,7 @@ export class Grid { if (typeof col.onallchecked === 'function') { col.onallchecked.call(this, col, flag); } else { - for (let row of this.#currentSource) { + for (let row of this._var.currentSource) { const item = row.values; if (item == null) { continue; @@ -1370,24 +1434,25 @@ export class Grid { } } - #onScroll(e) { - const left = e.target.scrollLeft; - if (this.#scrollLeft !== left) { - this.#scrollLeft = left; - this.#refs.header.style.left = `${-left}px`; + _onScroll(e) { + if (this._var.colAttrs.__filtering != null) { + this._onCloseFilter(); + } + if (this.onscrollbody === 'function') { + this.onscrollbody(e); } if (!this.virtual) { return; } const top = e.target.scrollTop; - this.#scrollToTop(top); + this._scrollToTop(top); } - #onBodyMouseMove(e, holder) { + _onBodyMouseMove(e, holder) { if (e.target.classList.contains('ui-grid-hover-holder')) { return; } - let [parent, target] = this.#getRowTarget(e.target); + let [parent, target] = this._getRowTarget(e.target); if (parent == null) { delete holder.dataset.row; delete holder.dataset.col; @@ -1415,18 +1480,18 @@ export class Grid { holder.dataset.row = row; holder.dataset.col = col; holder.innerText = element.innerText; - const top = this.#refs.bodyContent.offsetTop + target.offsetTop; + const top = this._var.refs.body.offsetTop + target.offsetTop; let left = target.offsetLeft; let width = holder.offsetWidth; - if (width > this.#bodyClientWidth) { - width = this.#bodyClientWidth; + if (width > this._var.bodyClientWidth) { + width = this._var.bodyClientWidth; } - const maxleft = this.#bodyClientWidth + this.#scrollLeft - width; + const maxleft = this._var.bodyClientWidth + this._var.scrollLeft - width; if (left > maxleft) { left = maxleft; } const height = target.offsetHeight; - holder.style.cssText = `top: ${top}px; left: ${left}px; max-width: ${this.#bodyClientWidth}px; height: ${height - 2}px`; + holder.style.cssText = `top: ${top}px; left: ${left}px; max-width: ${this._var.bodyClientWidth}px; height: ${height - 2}px`; holder.classList.add('active'); } else if (holder.classList.contains('active')) { delete holder.dataset.row; @@ -1435,15 +1500,15 @@ export class Grid { } } - #onRowClicked(e, index, colIndex) { - const startIndex = this.#startIndex; + _onRowClicked(e, index, colIndex) { + const startIndex = this._var.startIndex; const selectedIndex = startIndex + index; if (typeof this.willSelect === 'function' && !this.willSelect(selectedIndex, colIndex)) { return; } // multi-select let flag = false; - const selectedIndexes = this.#selectedIndexes; + const selectedIndexes = this._var.selectedIndexes; if (this.multiSelect) { if (e.ctrlKey) { const i = selectedIndexes.indexOf(selectedIndex); @@ -1480,8 +1545,8 @@ export class Grid { if (this.readonly !== true) { this.refresh(); } else { - [...this.#refs.bodyContent.children].forEach((row, i) => { - if (selectedIndexes.indexOf(startIndex + i) >= 0) { + this.tableRows.forEach((row, i) => { + if (selectedIndexes.includes(startIndex + i)) { row.classList.add('selected'); } else if (row.classList.contains('selected')) { row.classList.remove('selected'); @@ -1492,7 +1557,7 @@ export class Grid { this.selectedRowChanged(selectedIndex); } } - this.#selectedColumnIndex = colIndex; + this._var.selectedColumnIndex = colIndex; if ((this.fullrowClick || colIndex >= 0) && e.buttons === 1 && typeof this.cellClicked === 'function') { if (this.cellClicked(selectedIndex, colIndex) === false) { e.stopPropagation(); @@ -1501,7 +1566,7 @@ export class Grid { } } - #onRowDblClicked(e) { + _onRowDblClicked(e) { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'LAYER' && e.target.className === 'ui-check-inner' || e.target.tagName === 'LABEL' && (e.target.className === 'ui-drop-text' || e.target.className === 'ui-drop-caret')) { return; } @@ -1510,18 +1575,18 @@ export class Grid { this.rowDblClicked(index); } if (typeof this.cellDblClicked === 'function') { - const colIndex = this.#selectedColumnIndex; + const colIndex = this._var.selectedColumnIndex; if (this.fullrowClick || colIndex >= 0) { this.cellDblClicked(index, colIndex); } } } - #onRowChanged(_e, index, col, value, cell, blur) { - if (this.#currentSource == null) { + _onRowChanged(_e, index, col, value, cell, blur) { + if (this._var.currentSource == null) { return; } - const row = this.#currentSource[this.#startIndex + index]; + const row = this._var.currentSource[this._var.startIndex + index]; const item = row.values; if (item == null) { return; diff --git a/lib/ui/media.js b/lib/ui/media.js index 1d32e35..20386ed 100644 --- a/lib/ui/media.js +++ b/lib/ui/media.js @@ -67,7 +67,7 @@ function getTimeLabel(time) { } export function createAudio(mime, url) { - if (mime === 'audio/amr' && typeof AMR !== 'undefined') { + if ((mime === 'audio/amr' || mime === '.amr') && typeof AMR !== 'undefined') { const timestamp = createElement('span', 'ui-media-timestamp'); timestamp.textContent = '00:00 / 00:00'; let context; diff --git a/lib/ui/popup.js b/lib/ui/popup.js index 3e1883f..dd7df7d 100644 --- a/lib/ui/popup.js +++ b/lib/ui/popup.js @@ -39,16 +39,17 @@ function trimPx(px) { } export class Popup { - #mask; - #option; - #bounds; - // #cursor; + _var = {}; + // _var.mask; + // _var.option; + // _var.bounds; + // _var.cursor; constructor(opts = {}) { - this.#option = opts; + this._var.option = opts; } - get container() { return this.#mask.querySelector('.ui-popup-container') } + get container() { return this._var.mask.querySelector('.ui-popup-container') } get rect() { const container = this.container; @@ -57,7 +58,7 @@ export class Popup { } const style = global.getComputedStyle(container); const collapsed = container.classList.contains('ui-popup-collapse'); - const bounds = this.#bounds; + const bounds = this._var.bounds; return { collapsed, left: trimPx(style.left), @@ -81,7 +82,7 @@ export class Popup { const collapse = container.querySelector('.ui-popup-header-icons>.icon-expand'); if (r.collapsed === true) { css.push('width: 160px', 'height: 40px'); - this.#bounds = r; + this._var.bounds = r; container.classList.add('ui-popup-collapse'); if (collapse != null) { changeIcon(collapse, 'fa-regular', 'expand-alt'); @@ -94,7 +95,7 @@ export class Popup { css.push(`height: ${r.height}px`); } container.classList.remove('ui-popup-collapse'); - this.#bounds = null; + this._var.bounds = null; if (collapse != null) { changeIcon(collapse, 'fa-regular', 'compress-alt'); } @@ -105,7 +106,7 @@ export class Popup { } close(animation = true) { - const mask = this.#mask; + const mask = this._var.mask; if (animation) { mask.classList.add('ui-popup-active'); mask.style.opacity = 0; @@ -113,17 +114,17 @@ export class Popup { } else { mask.remove(); } - if (typeof this.#option.onMasking === 'function') { - this.#option.onMasking.call(this, false); + if (typeof this._var.option.onMasking === 'function') { + this._var.option.onMasking.call(this, false); } - if (typeof this.#option.resolve === 'function') { - this.#option.resolve(); + if (typeof this._var.option.resolve === 'function') { + this._var.option.resolve(); } } create() { const mask = createElement('div', 'ui-popup-mask'); - const option = this.#option; + const option = this._var.option; if (option.mask === false) { mask.classList.add('ui-popup-transparent'); } else if (typeof option.onMasking === 'function') { @@ -135,7 +136,7 @@ export class Popup { const container = createElement('div', 'ui-popup-container'); if (option.changeZIndex === true) { container.addEventListener('mousedown', () => { - const masks = [...this.#mask.parentElement.children].filter(e => e.classList.contains('ui-popup-mask')); + const masks = [...this._var.mask.parentElement.children].filter(e => e.classList.contains('ui-popup-mask')); let max = 200; masks.forEach(m => { let index; @@ -223,16 +224,16 @@ export class Popup { }); collapse.addEventListener('click', () => { if (container.classList.contains('ui-popup-collapse')) { - const bounds = this.#bounds; + const bounds = this._var.bounds; if (bounds != null) { container.style.cssText += `width: ${bounds.width}px; height: ${bounds.height}px`; - this.#bounds = null; + this._var.bounds = null; } container.classList.remove('ui-popup-collapse'); changeIcon(collapse, 'fa-regular', 'compress-alt'); } else { const rect = this.rect; - this.#bounds = rect; + this._var.bounds = rect; container.style.cssText += `width: 160px; height: 40px`; container.classList.add('ui-popup-collapse'); changeIcon(collapse, 'fa-regular', 'expand-alt'); @@ -310,40 +311,40 @@ export class Popup { container.append( createElement('layer', layer => { layer.className = 'ui-popup-border ui-popup-border-right'; - layer.addEventListener('mousedown', e => this.#resize(ResizeMods.right, e)); + layer.addEventListener('mousedown', e => this._resize(ResizeMods.right, e)); }), createElement('layer', layer => { layer.className = 'ui-popup-border ui-popup-border-bottom'; - layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottom, e)); + layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottom, e)); }), createElement('layer', layer => { layer.className = 'ui-popup-border ui-popup-border-left'; - layer.addEventListener('mousedown', e => this.#resize(ResizeMods.left, e)); + layer.addEventListener('mousedown', e => this._resize(ResizeMods.left, e)); }), createElement('layer', layer => { layer.className = 'ui-popup-border ui-popup-border-top'; - layer.addEventListener('mousedown', e => this.#resize(ResizeMods.top, e)); + layer.addEventListener('mousedown', e => this._resize(ResizeMods.top, e)); }), createElement('layer', layer => { layer.className = 'ui-popup-border ui-popup-border-bottom-right'; - layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottomRight, e)); + layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomRight, e)); }), createElement('layer', layer => { layer.className = 'ui-popup-border ui-popup-border-bottom-left'; - layer.addEventListener('mousedown', e => this.#resize(ResizeMods.bottomLeft, e)); + layer.addEventListener('mousedown', e => this._resize(ResizeMods.bottomLeft, e)); }), createElement('layer', layer => { layer.className = 'ui-popup-border ui-popup-border-top-left'; - layer.addEventListener('mousedown', e => this.#resize(ResizeMods.topLeft, e)); + layer.addEventListener('mousedown', e => this._resize(ResizeMods.topLeft, e)); }), createElement('layer', layer => { layer.className = 'ui-popup-border ui-popup-border-top-right'; - layer.addEventListener('mousedown', e => this.#resize(ResizeMods.topRight, e)); + layer.addEventListener('mousedown', e => this._resize(ResizeMods.topRight, e)); }) ) } mask.appendChild(container); - this.#mask = mask; + this._var.mask = mask; return mask; } @@ -351,7 +352,7 @@ export class Popup { if (parent == null) { return; } - let mask = this.#mask ?? this.create(); + let mask = this._var.mask ?? this.create(); // const exists = [...parent.children].filter(e => e.classList.contains('ui-popup-mask')); const exists = parent.querySelectorAll('.ui-popup-mask'); let zindex = 0; @@ -365,7 +366,7 @@ export class Popup { mask.style.zIndex = String(zindex + 1); } parent.appendChild(mask); - if (this.#option.mask === false) { + if (this._var.option.mask === false) { // calculator position const container = this.container; container.style.left = String((parent.offsetWidth - container.offsetWidth) / 2) + 'px'; @@ -380,9 +381,9 @@ export class Popup { }); } - get loading() { return this.#mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' } + get loading() { return this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' } set loading(flag) { - let loading = this.#mask?.querySelector('.ui-popup-body>.ui-popup-loading'); + let loading = this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading'); if (loading == null) { return; } @@ -395,17 +396,17 @@ export class Popup { } } - #resize(mod, e) { + _resize(mod, e) { if (e.buttons !== 1) { return; } const container = this.container; - const option = this.#option; + const option = this._var.option; if (typeof option.onResizeStarted === 'function') { option.onResizeStarted.call(this); } - const mask = this.#mask; - // this.#cursor = mask.style.cursor; + const mask = this._var.mask; + // this._var.cursor = mask.style.cursor; // mask.style.cursor = Cursors[mod]; const originalX = e.clientX; const originalY = e.clientY; @@ -471,7 +472,7 @@ export class Popup { const up = () => { parent.removeEventListener('mousemove', move, { passive: false }); parent.removeEventListener('mouseup', up); - // mask.style.cursor = this.#cursor; + // mask.style.cursor = this._var.cursor; if (resized === true && typeof option.onResizeEnded === 'function') { option.onResizeEnded.call(this); } diff --git a/lib/ui/tooltip.d.ts b/lib/ui/tooltip.d.ts index 456e354..4884310 100644 --- a/lib/ui/tooltip.d.ts +++ b/lib/ui/tooltip.d.ts @@ -2,8 +2,8 @@ * 为元素设置一个 tooltip * @param container 要设置 tooltip 的容器元素 * @param content 提示内容,可以是字符串也可以是 html 元素 - * @param flag 是否仅在元素内容没有呈现完全时显示 tooltip - * @param parent 呈现在哪个父元素上,默认添加在 `container` 末端 + * @param [flag=true] 是否仅在元素内容没有呈现完全时显示 tooltip + * @param [parent=null] 呈现在哪个父元素上,默认添加在 `container` 末端 */ export function setTooltip(container: HTMLElement, content: string | HTMLElement, flag?: boolean, parent?: HTMLElement): HTMLElement diff --git a/lib/ui/tooltip.js b/lib/ui/tooltip.js index 603f3f2..4955087 100644 --- a/lib/ui/tooltip.js +++ b/lib/ui/tooltip.js @@ -91,6 +91,9 @@ export function setTooltip(container, content, flag = false, parent = null) { let t = c.offsetTop; let l = c.offsetLeft; p = c.offsetParent; + if (p == null) { + return; + } let lastWidth = p.clientWidth; let lastHeight = p.clientHeight; while (p != null) { diff --git a/lib/utility/cookie.js b/lib/utility/cookie.js index 295692b..83ecf68 100644 --- a/lib/utility/cookie.js +++ b/lib/utility/cookie.js @@ -22,7 +22,7 @@ export function getCookie(name) { const cookies = document.cookie.split(';'); for (let cookie of cookies) { cookie = cookie.trim(); - if (cookie.indexOf(name) === 0) { + if (cookie.startsWith(name)) { return decodeURIComponent(cookie.substring(name.length)); } } diff --git a/lib/utility/strings.js b/lib/utility/strings.js index 36e0486..8ab6464 100644 --- a/lib/utility/strings.js +++ b/lib/utility/strings.js @@ -10,16 +10,16 @@ export function contains(s, key, ignoreCase) { key = String(key); } if (ignoreCase) { - return s.toLowerCase().indexOf(key.toLowerCase()) >= 0; + return s.toLowerCase().includes(key.toLowerCase()); } - return s.indexOf(key) >= 0; + return s.includes(key); } export function endsWith(s, suffix) { if (nullOrEmpty(s) || nullOrEmpty(suffix)) { return false; } - return s.indexOf(suffix) === s.length - suffix.length; + return s.endsWith(suffix); // === s.length - suffix.length; } export function padStart(s, num, char) { diff --git a/main.js b/main.js index e7884e3..1367e67 100644 --- a/main.js +++ b/main.js @@ -1,23 +1,35 @@ import './style.scss' // import javascriptLogo from './javascript.svg' -import { get } from './lib/utility' -import { createPicture, createAudio, createVideo, createPdf } from './lib/ui/media' +// import { get } from './lib/utility' +// import { createPicture, createAudio, createVideo, createPdf } from './lib/ui/media' +import './lib/element/style.scss' +import ScheduleItem from './lib/element/schedule' +import { createElement } from './lib/functions'; // document.querySelector('#js-logo').src = javascriptLogo window.consts = { path: '/', - resver: 20230329 + resver: 20231218 } +const schedule = new ScheduleItem(); document.querySelector('#container').replaceChildren( - // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/1740581frZuuFhz5WWCysxs9oGB.jpg'), - createAudio('audio/amr', 'http://vite.tsanie.org/1055003tb0DisaMu1615PeSXKG.amr'), - createPdf('AG-PRO COMPANIES', 'https://fleet.foresightintelligence.com/doc/mmspart/1333321JLrYhkGYqsw6QSVMx3d.pdf'), - // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/138390UGZUMWRmqBsEgPnWuW16.gif'), - // createVideo('https://fleet.foresightintelligence.com/doc/mmspart/17359338sR5qsG7TvS7eaUdP9PL.mp4'), + schedule.create(), + createElement('button', button => { + button.innerText = 'Get'; + button.addEventListener('click', () => console.log(schedule.getParameters())); + }) ); +// document.querySelector('#container').replaceChildren( +// // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/1740581frZuuFhz5WWCysxs9oGB.jpg'), +// createAudio('audio/amr', 'http://vite.tsanie.org/1055003tb0DisaMu1615PeSXKG.amr'), +// createPdf('AG-PRO COMPANIES', 'https://fleet.foresightintelligence.com/doc/mmspart/1333321JLrYhkGYqsw6QSVMx3d.pdf'), +// // createPicture('https://fleet.foresightintelligence.com/doc/mmspart/138390UGZUMWRmqBsEgPnWuW16.gif'), +// // createVideo('https://fleet.foresightintelligence.com/doc/mmspart/17359338sR5qsG7TvS7eaUdP9PL.mp4'), +// ); + /* init(null, { template: '/res.json', diff --git a/style.css b/style.css new file mode 100644 index 0000000..0ab2080 --- /dev/null +++ b/style.css @@ -0,0 +1 @@ +@keyframes loading-spinner{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}:root{--color: #201f1e;--secondary-color: #777;--bg-color: #fff;--border-color: #b9b9b9;--focus-border-color: #666;--shadow-color: rgba(0 0 0 /11%);--disabled-color: #aaa;--disabled-bg-color: #e9e9e9;--disabled-border-color: #d9d9d9;--red-color: red;--title-color: #fff;--title-bg-color: rgb(68, 114, 196);--hover-bg-color: #eee;--link-color: #1890ff;--secondary-link-color: #1d9ac0;--primary-color: rgb(123, 28, 33);--loading-bg-color: hsla(0, 0%, 100%, .4);--loading-fore-color: rgba(0, 0, 0, .2);--border-radius: 2px;--text-indent: 4px;--line-height: 24px;--font-size: .8125rem;--font-smaller-size: .75rem;--font-larger-size: .875rem;--font-family: "Franklin Gothic Book", "San Francisco", "Segoe UI", "Open Sans", "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei UI", sans-serif}.ui-text,.ui-input[type]{font-size:var(--font-size);font-family:var(--font-family);border:1px solid var(--border-color);border-radius:var(--border-radius);transition:border-color .12s ease}.ui-text:focus,.ui-text:focus-visible,.ui-input[type]:focus,.ui-input[type]:focus-visible{outline:none}.ui-text:focus,.ui-text:hover,.ui-input[type]:focus,.ui-input[type]:hover{border-color:var(--focus-border-color)}.ui-text:disabled,.ui-input[type]:disabled{border-color:var(--disabled-border-color);color:var(--disabled-color);background-color:var(--disabled-bg-color)}.ui-input{text-indent:var(--text-indent);line-height:var(--line-height)}.ui-check-image-wrapper>input[type=checkbox]{display:none}.ui-check-image-wrapper>input[type=checkbox]:checked~.checked{display:inline}.ui-check-image-wrapper>input[type=checkbox]:checked~.unchecked{display:none}.ui-check-image-wrapper>.checked{display:none}.ui-check-image-wrapper>.unchecked{display:inline}.ui-check-wrapper{display:inline-flex;align-items:center;padding:0 8px;height:36px}.ui-check-wrapper .ui-check-inner{position:relative;display:inline-block;padding:0;width:14px;height:14px;background-color:#fff;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:pointer;border:1px solid var(--border-color);border-radius:var(--border-radius);transition:border-color .12s ease}.ui-check-wrapper .ui-check-inner:focus,.ui-check-wrapper .ui-check-inner:focus-visible{outline:none}.ui-check-wrapper .ui-check-inner:focus,.ui-check-wrapper .ui-check-inner:hover{border-color:var(--focus-border-color)}.ui-check-wrapper .ui-check-inner:disabled{border-color:var(--disabled-border-color);color:var(--disabled-color);background-color:var(--disabled-bg-color)}.ui-check-wrapper .ui-check-inner>svg{position:absolute;top:0;left:0;width:100%;height:100%;fill:#fff;transform:scale(0);opacity:0;transition:all .08s cubic-bezier(0.78, 0.14, 0.15, 0.86)}.ui-check-wrapper.ui-radio-wrapper .ui-check-inner{box-sizing:border-box;border-radius:8px;width:16px;height:16px}.ui-check-wrapper>input[type=checkbox],.ui-check-wrapper>input[type=radio]{display:none}.ui-check-wrapper>input[type=checkbox]:checked+.ui-check-inner,.ui-check-wrapper>input[type=radio]:checked+.ui-check-inner{border-color:var(--link-color);background-color:var(--link-color)}.ui-check-wrapper>input[type=checkbox]:checked+.ui-check-inner>svg,.ui-check-wrapper>input[type=radio]:checked+.ui-check-inner>svg{transform:scale(1);opacity:1}.ui-check-wrapper>input[type=checkbox]:disabled:checked+.ui-check-inner,.ui-check-wrapper>input[type=radio]:disabled:checked+.ui-check-inner{border-color:var(--disabled-border-color);background-color:var(--disabled-border-color)}.ui-check-wrapper>input[type=checkbox]:disabled+.ui-check-inner,.ui-check-wrapper>input[type=radio]:disabled+.ui-check-inner{border-color:var(--disabled-border-color);background-color:var(--disabled-bg-color);cursor:default}.ui-check-wrapper>input[type=checkbox]:disabled~span,.ui-check-wrapper>input[type=radio]:disabled~span{color:var(--disabled-border-color);cursor:default}.ui-check-wrapper .ui-check-inner{flex:0 0 auto}.ui-check-wrapper .ui-check-inner+*{flex:1 1 auto;font-weight:400;font-size:var(--font-size);padding-left:8px;padding-right:6px;align-self:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;color:var(--color)}.ui-drop-wrapper{display:inline-block;border:none;border-radius:unset;-webkit-user-select:none;-moz-user-select:none;user-select:none;position:relative;font-size:var(--font-size);font-family:var(--font-family)}.ui-drop-wrapper>.ui-drop-header{background-color:var(--bg-color);display:flex;height:26px;border:1px solid var(--border-color);border-radius:var(--border-radius);transition:border-color .12s ease}.ui-drop-wrapper>.ui-drop-header:focus,.ui-drop-wrapper>.ui-drop-header:focus-visible{outline:none}.ui-drop-wrapper>.ui-drop-header:focus,.ui-drop-wrapper>.ui-drop-header:hover{border-color:var(--focus-border-color)}.ui-drop-wrapper>.ui-drop-header:disabled{border-color:var(--disabled-border-color);color:var(--disabled-color);background-color:var(--disabled-bg-color)}.ui-drop-wrapper>.ui-drop-header>.ui-drop-text{flex:1 1 auto;cursor:pointer;font-size:var(--font-size);padding:0 6px;overflow:hidden;text-overflow:ellipsis;border:none;white-space:nowrap}.ui-drop-wrapper>.ui-drop-header>.ui-drop-text:focus,.ui-drop-wrapper>.ui-drop-header>.ui-drop-text:focus-visible{outline:none}.ui-drop-wrapper>.ui-drop-header>input.ui-drop-text{cursor:initial}.ui-drop-wrapper>.ui-drop-header>input.ui-drop-text::-moz-placeholder{font-size:var(--font-smaller-size);font-style:italic}.ui-drop-wrapper>.ui-drop-header>input.ui-drop-text::placeholder{font-size:var(--font-smaller-size);font-style:italic}.ui-drop-wrapper>.ui-drop-header>.ui-drop-caret{flex:0 0 auto;width:26px;display:flex;justify-content:center;align-items:center;cursor:pointer}.ui-drop-wrapper>.ui-drop-header>.ui-drop-caret::after{display:block;content:"";border-top:4px solid;border-left:4px solid rgba(0,0,0,0);border-right:4px solid rgba(0,0,0,0);height:0;width:0}.ui-drop-wrapper>.ui-drop-header.disabled{border-color:var(--disabled-border-color);background-color:var(--disabled-bg-color);color:var(--disabled-color)}.ui-drop-wrapper>.ui-drop-header.disabled:focus{border-color:var(--disabled-border-color)}.ui-drop-wrapper>.ui-drop-header.disabled>.ui-drop-text,.ui-drop-wrapper>.ui-drop-header.disabled>.ui-drop-caret{cursor:default}.ui-drop-wrapper>.ui-drop-box{position:absolute;visibility:hidden;opacity:0;transform:scaleY(0);transform-origin:top;background-color:var(--bg-color);top:28px;z-index:2;transition:transform 120ms ease,opacity 120ms ease,visibility 120ms ease;width:calc(100% + 2px);box-sizing:border-box;box-shadow:0 3px 6px -4px rgba(0,0,0,.12),0 6px 16px 0 rgba(0,0,0,.08),0 9px 28px 8px rgba(0,0,0,.05);left:-1px}.ui-drop-wrapper>.ui-drop-box.slide-up{transform-origin:bottom;top:unset;bottom:28px}.ui-drop-wrapper>.ui-drop-box.active{visibility:visible;opacity:1;transform:scaleY(1)}.ui-drop-wrapper>.ui-drop-box>.ui-drop-search{box-sizing:border-box;height:36px;line-height:36px;padding:0 8px;position:relative;display:flex;align-items:center}.ui-drop-wrapper>.ui-drop-box>.ui-drop-search>input[type=text]{box-sizing:border-box;width:100%;height:26px;padding:0 6px 0 22px;color:var(--color);border:1px solid var(--border-color);border-radius:var(--border-radius);transition:border-color .12s ease}.ui-drop-wrapper>.ui-drop-box>.ui-drop-search>input[type=text]:focus,.ui-drop-wrapper>.ui-drop-box>.ui-drop-search>input[type=text]:focus-visible{outline:none}.ui-drop-wrapper>.ui-drop-box>.ui-drop-search>input[type=text]:focus,.ui-drop-wrapper>.ui-drop-box>.ui-drop-search>input[type=text]:hover{border-color:var(--focus-border-color)}.ui-drop-wrapper>.ui-drop-box>.ui-drop-search>input[type=text]:disabled{border-color:var(--disabled-border-color);color:var(--disabled-color);background-color:var(--disabled-bg-color)}.ui-drop-wrapper>.ui-drop-box>.ui-drop-search>input[type=text]::-moz-placeholder{font-style:italic}.ui-drop-wrapper>.ui-drop-box>.ui-drop-search>input[type=text]::placeholder{font-style:italic}.ui-drop-wrapper>.ui-drop-box>.ui-drop-search>svg{position:absolute;left:14px;width:13px;height:100%;cursor:text}.ui-drop-wrapper>.ui-drop-box>.ui-drop-list{margin:0;padding:0;list-style:none;max-height:210px;overflow-y:auto;font-size:var(--font-size)}.ui-drop-wrapper>.ui-drop-box>.ui-drop-list::-webkit-scrollbar{width:8px;height:8px}.ui-drop-wrapper>.ui-drop-box>.ui-drop-list::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-drop-wrapper>.ui-drop-box>.ui-drop-list.filtered>li:first-child{background-color:var(--hover-bg-color)}.ui-drop-wrapper>.ui-drop-box>.ui-drop-list>li{line-height:30px;height:30px;padding:0 10px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ui-drop-wrapper>.ui-drop-box>.ui-drop-list>li:hover,.ui-drop-wrapper>.ui-drop-box>.ui-drop-list>li.selected{background-color:var(--hover-bg-color)}.ui-drop-wrapper>.ui-drop-box>.ui-drop-list>li>.ui-check-wrapper{height:30px;display:flex}.ui-grid{position:relative;box-sizing:border-box;display:flex;flex-direction:column;overflow:visible;overflow-x:hidden}.ui-grid{--cell-hover-bg-color: lightyellow;--header-border-color: #adaba9;--header-bg-color: #fafafa;--header-fore-color: #000;--cell-border-color: #f0f0f0;--cell-fore-color: #333;--dark-border-color: #666;--split-border-color: #b3b3b3;--dragger-bg-color: #fff;--dragger-cursor-color: #333;--row-bg-color: #fff;--row-active-bg-color: #fafafa;--row-selected-bg-color: #e6f2fb;--text-disabled-color: gray;--filter-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05);--filter-transition: transform .12s ease, opacity .24s ease;--row-height: 36px;--header-line-height: 26px;--text-indent: 8px;--loading-size: 40px;--loading-border-radius: 20px;--arrow-size: 4px;--filter-size: 10px;--split-width: 8px;--dragger-size: 20px;--dragger-opacity: .6;--dragger-cursor-size: 4px;--dragger-cursor-pos: -4px;--dragger-cursor-opacity: .3;--header-padding: 4px 12px 4px 8px;--header-filter-padding: 4px 26px 4px 8px;--spacing-s: 4px;--spacing-cell: 6px 4px 6px 8px;--filter-line-height: 30px;--filter-item-padding: 0 4px}.ui-grid:focus,.ui-grid:focus-visible{outline:none}.ui-grid,.ui-grid input[type=text],.ui-grid textarea{font-size:var(--font-size);font-family:var(--font-family)}.ui-grid>.ui-grid-sizer{position:absolute;white-space:nowrap;font-weight:bold;visibility:hidden}.ui-grid>.ui-grid-header{width:100%;min-width:100%;margin:0;border-bottom:1px solid var(--header-border-color);background-color:var(--header-bg-color);color:var(--header-fore-color);-webkit-user-select:none;-moz-user-select:none;user-select:none;border-collapse:collapse;border-spacing:0;table-layout:fixed}.ui-grid>.ui-grid-header tr{position:relative}.ui-grid>.ui-grid-header tr>th{padding:0;margin:0;word-wrap:break-word;white-space:normal;position:relative}.ui-grid>.ui-grid-header tr>th>div{line-height:var(--header-line-height);min-height:var(--row-height);display:flex;align-items:center;padding:var(--header-padding);box-sizing:border-box}.ui-grid>.ui-grid-header tr>th>div>span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.ui-grid>.ui-grid-header tr>th>.arrow{width:0;height:0;top:50%;margin-top:calc(0px - var(--arrow-size)/2);right:calc(var(--arrow-size)/2);position:absolute}.ui-grid>.ui-grid-header tr>th>.arrow.asc{border-bottom:var(--arrow-size) solid var(--dark-border-color)}.ui-grid>.ui-grid-header tr>th>.arrow.desc{border-top:var(--arrow-size) solid var(--dark-border-color)}.ui-grid>.ui-grid-header tr>th>.arrow.asc,.ui-grid>.ui-grid-header tr>th>.arrow.desc{border-left:var(--arrow-size) solid rgba(0,0,0,0);border-right:var(--arrow-size) solid rgba(0,0,0,0)}.ui-grid>.ui-grid-header tr>th>.filter{width:var(--filter-size);height:var(--filter-size);top:50%;margin-top:calc(0px - var(--filter-size)/2);right:calc(var(--arrow-size)*2 + 4px);position:absolute;display:flex}.ui-grid>.ui-grid-header tr>th>.filter>svg{width:100%;height:100%;fill:var(--color);opacity:.2;transition:opacity .12s ease}.ui-grid>.ui-grid-header tr>th>.filter>svg:hover{opacity:.8}.ui-grid>.ui-grid-header tr>th>.filter.hover>svg{opacity:.8}.ui-grid>.ui-grid-header tr>th>.filter.active>svg{opacity:1}.ui-grid>.ui-grid-header tr>th>.spliter{position:absolute;height:100%;top:0;right:calc(0px - var(--split-width)/2);width:var(--split-width);cursor:ew-resize;z-index:1}.ui-grid>.ui-grid-header tr>th>.spliter::after{content:"";height:100%;width:1px;display:block;margin:0 auto;transition:background-color .12s ease}.ui-grid>.ui-grid-header tr>th>.spliter:hover::after{background-color:var(--split-border-color)}.ui-grid>.ui-grid-header tr>th>.dragger{position:absolute;left:0;top:0;min-width:var(--dragger-size);height:100%;background-color:var(--dragger-bg-color);opacity:var(--dragger-opacity);display:none}.ui-grid>.ui-grid-header tr>th>.dragger-cursor{position:absolute;top:0;height:100%;border:1px solid var(--dragger-cursor-color);box-sizing:border-box;margin-left:0;opacity:var(--dragger-cursor-opacity);display:none;transition:left .12s ease}.ui-grid>.ui-grid-header tr>th>.dragger-cursor::before{top:-1px;border-top:var(--dragger-cursor-size) solid}.ui-grid>.ui-grid-header tr>th>.dragger-cursor::after{bottom:-1px;border-bottom:var(--dragger-cursor-size) solid}.ui-grid>.ui-grid-header tr>th>.dragger-cursor::before,.ui-grid>.ui-grid-header tr>th>.dragger-cursor::after{content:"";position:absolute;left:var(--dragger-cursor-pos);border-left:var(--dragger-cursor-size) solid rgba(0,0,0,0);border-right:var(--dragger-cursor-size) solid rgba(0,0,0,0)}.ui-grid>.ui-grid-header tr>th.header-filter>div{padding:var(--header-filter-padding)}.ui-grid>.ui-grid-body{flex:1 1 auto;overflow:auto;color:var(--cell-fore-color)}.ui-grid>.ui-grid-body::-webkit-scrollbar{width:8px;height:8px}.ui-grid>.ui-grid-body::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-grid>.ui-grid-body .ui-grid-body-content{position:absolute;min-width:100%;table-layout:fixed;border-collapse:collapse;border-spacing:0}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row{line-height:var(--line-height);white-space:nowrap;background-color:var(--row-bg-color);border-bottom:1px solid var(--cell-border-color);box-sizing:border-box}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row:hover{background-color:var(--row-active-bg-color)}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row.selected{background-color:var(--row-selected-bg-color)}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td{padding:0}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>span{padding:var(--spacing-cell);display:block;overflow:hidden;text-overflow:ellipsis;white-space:pre}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>input[type=text],.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>textarea{border:none;box-sizing:border-box;width:100%;padding:0}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>input[type=text]:focus,.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>input[type=text]:focus-visible,.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>textarea:focus,.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>textarea:focus-visible{outline:none}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>input[type=text]:disabled,.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>textarea:disabled{color:var(--text-disabled-color)}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>input[type=text]{height:var(--row-height);text-indent:var(--text-indent)}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>textarea{resize:none;line-height:var(--line-height);display:block;padding:var(--spacing-cell);white-space:nowrap}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>textarea::-webkit-scrollbar{width:8px;height:8px}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td>textarea::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .ui-check-wrapper{display:flex;justify-content:center}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .ui-check-wrapper .ui-check-inner,.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .ui-check-wrapper .ui-check-inner>svg{transition:none}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .ui-drop-wrapper{height:var(--row-height);width:100%;display:flex;flex-direction:column}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-header{border:none;height:100%}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-header>.ui-drop-text{padding:var(--spacing-cell)}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-box{top:calc(var(--row-height) + 2px)}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .ui-drop-wrapper>.ui-drop-box.slide-up{top:unset;bottom:calc(var(--row-height) + 2px)}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .col-icon{display:flex;cursor:pointer;justify-content:center;align-items:center;position:relative}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .col-icon>svg{width:16px;height:16px;fill:var(--primary-color);transition:opacity .12s ease}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .col-icon:hover>svg{opacity:.4}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .col-icon.disabled{cursor:unset}.ui-grid>.ui-grid-body .ui-grid-body-content>.ui-grid-row>td .col-icon.disabled>svg{fill:var(--header-border-color);opacity:unset}.ui-grid>.ui-grid-body .ui-grid-hover-holder{box-sizing:border-box;position:absolute;line-height:var(--line-height);padding:var(--spacing-cell);background-color:var(--cell-hover-bg-color);white-space:pre;display:flex;align-items:center;visibility:hidden;opacity:0;transition:visibility 0s linear .12s,opacity .12s ease}.ui-grid>.ui-grid-body .ui-grid-hover-holder.active{visibility:visible;opacity:1}.ui-grid>.ui-grid-loading{position:absolute;top:0;right:0;bottom:0;left:0;visibility:hidden;opacity:0;transition:visibility 0s linear .12s,opacity .12s ease;background-color:var(--loading-bg-color);display:flex;justify-content:center;align-items:center;z-index:1}.ui-grid>.ui-grid-loading>div{background-color:var(--loading-fore-color);border-radius:var(--loading-border-radius)}.ui-grid>.ui-grid-loading>div>svg{width:var(--loading-size);height:var(--loading-size);padding:20px;animation:loading-spinner 1.2s infinite linear}.ui-grid>.filter-panel{position:absolute;width:200px;height:300px;box-shadow:var(--filter-shadow);transition:var(--filter-transition);background-color:var(--bg-color);transform:scaleY(0);transform-origin:top;opacity:0;display:flex;flex-direction:column}.ui-grid>.filter-panel.active{transform:scaleY(1);opacity:1}.ui-grid>.filter-panel>.filter-search-holder{position:relative;margin:8px 8px 4px}.ui-grid>.filter-panel>.filter-search-holder>.filter-search-box{box-sizing:border-box;text-indent:16px;width:100%;font-size:var(--font-smaller-size);height:var(--line-height);line-height:var(--line-height)}.ui-grid>.filter-panel>.filter-search-holder>svg{position:absolute;width:12px;height:12px;top:calc(50% - 6px);left:4px;fill:var(--color);cursor:text}.ui-grid>.filter-panel>.filter-item-list{flex:1 1 auto;overflow-y:auto;overflow-x:hidden;position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none}.ui-grid>.filter-panel>.filter-item-list::-webkit-scrollbar{width:8px;height:8px}.ui-grid>.filter-panel>.filter-item-list::-webkit-scrollbar-thumb{background-color:rgba(168,168,168,.9);border-radius:4px}.ui-grid>.filter-panel>.filter-item-list>.filter-content{position:absolute;width:100%}.ui-grid>.filter-panel>.filter-item-list .filter-item{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;padding:var(--filter-item-padding)}.ui-grid>.filter-panel>.filter-item-list .filter-item:hover{background-color:var(--hover-bg-color)}.ui-grid>.filter-panel>.filter-item-list .filter-item .ui-check-wrapper{height:var(--filter-line-height);display:flex}.ui-grid>.filter-panel>.filter-item-list .filter-item .ui-check-wrapper .ui-check-inner+*{font-size:var(--font-smaller-size)}.ui-grid>.filter-panel>.filter-function{display:flex;justify-content:flex-end;padding:4px}.ui-grid>.filter-panel>.filter-function>button{box-sizing:border-box;margin-right:10px;min-width:40px;height:var(--filter-line-height);border:none;background-color:rgba(0,0,0,0);cursor:pointer;border-radius:0;transition:background-color .12s ease}.ui-grid>.filter-panel>.filter-function>button:focus,.ui-grid>.filter-panel>.filter-function>button:focus-visible{outline:none}.ui-grid>.filter-panel>.filter-function>button:hover{background-color:var(--hover-bg-color)}.ui-popup-mask{position:fixed;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,.2);display:flex;justify-content:center;align-items:center;opacity:0;z-index:200;transition:opacity .12s ease}.ui-popup-mask{--corner-radius: 6px;--loading-size: 20px;--loading-border-radius: 10px}.ui-popup-mask.ui-popup-active .ui-popup-container{transform:scale(1.1)}.ui-popup-mask.ui-popup-transparent{right:unset;bottom:unset}.ui-popup-mask .ui-popup-container{min-width:400px;max-width:800px;max-height:90vh;background-color:var(--bg-color);border-radius:var(--corner-radius);box-shadow:0 2px 8px var(--shadow-color);transition:opacity .12s ease,transform .12s ease;position:absolute;display:flex;flex-direction:column}.ui-popup-mask .ui-popup-container:focus,.ui-popup-mask .ui-popup-container:focus-visible{outline:none}.ui-popup-mask .ui-popup-container .ui-popup-header{flex:0 0 auto;border-radius:var(--corner-radius) var(--corner-radius) 0 0;line-height:24px;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:var(--title-ctrlbg-color);color:var(--title-color);display:flex;align-items:center}.ui-popup-mask .ui-popup-container .ui-popup-header>div{flex:1 1 auto;font-size:1rem}.ui-popup-mask .ui-popup-container .ui-popup-header>.ui-popup-header-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:10px 0 6px 12px}.ui-popup-mask .ui-popup-container .ui-popup-header>.ui-popup-header-title,.ui-popup-mask .ui-popup-container .ui-popup-header .ui-popup-move{cursor:move}.ui-popup-mask .ui-popup-container .ui-popup-header>.ui-popup-header-icons{flex:0 0 auto;padding:10px 12px 6px 0;display:flex}.ui-popup-mask .ui-popup-container .ui-popup-header>.ui-popup-header-icons>svg{flex:0 0 auto;width:24px;height:24px;fill:var(--title-color);padding:4px;cursor:pointer;box-sizing:border-box;transition:opacity .12s ease}.ui-popup-mask .ui-popup-container .ui-popup-header>.ui-popup-header-icons>svg:hover{opacity:.8}.ui-popup-mask .ui-popup-container .ui-popup-header>.ui-popup-header-icons>svg:focus,.ui-popup-mask .ui-popup-container .ui-popup-header>.ui-popup-header-icons>svg:focus-visible{outline:none;opacity:.8;background-color:rgba(0,0,0,.1);border-radius:var(--corner-radius)}.ui-popup-mask .ui-popup-container .ui-popup-body{margin:6px 10px;flex:1 1 auto;line-height:24px;position:relative;min-height:100px}.ui-popup-mask .ui-popup-container .ui-popup-body>.ui-popup-loading{position:absolute;top:0;right:0;bottom:-46px;left:0;visibility:hidden;opacity:0;transition:visibility 0s linear .12s,opacity .12s ease;background-color:var(--loading-bg-color);display:flex;justify-content:center;align-items:center;z-index:1}.ui-popup-mask .ui-popup-container .ui-popup-body>.ui-popup-loading>div{background-color:var(--loading-fore-color);border-radius:var(--loading-border-radius)}.ui-popup-mask .ui-popup-container .ui-popup-body>.ui-popup-loading>div>svg{width:var(--loading-size);height:var(--loading-size);padding:20px;animation:loading-spinner 1.2s infinite linear}.ui-popup-mask .ui-popup-container .ui-popup-body>.ui-popup-loading.ui-popup-loading-content{bottom:0}.ui-popup-mask .ui-popup-container .ui-popup-body>.message-wrapper{display:flex;margin:10px}.ui-popup-mask .ui-popup-container .ui-popup-body>.message-wrapper>svg{width:40px;height:40px}.ui-popup-mask .ui-popup-container .ui-popup-body>.message-wrapper>svg+span{padding-left:16px}.ui-popup-mask .ui-popup-container .ui-popup-body .setting-wrapper{--line-height: 28px}.ui-popup-mask .ui-popup-container .ui-popup-body .setting-wrapper>.setting-item{display:flex;align-items:center;line-height:var(--line-height);margin:4px 0}.ui-popup-mask .ui-popup-container .ui-popup-body .setting-wrapper>.setting-item>.setting-label{flex:0 0 auto;width:120px;text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:10px}.ui-popup-mask .ui-popup-container .ui-popup-body .setting-wrapper>.setting-item>.setting-label.setting-required::after{content:"*";color:var(--red-color);font-weight:bold}.ui-popup-mask .ui-popup-container .ui-popup-body .setting-wrapper>.setting-item>.setting-label+*{flex:1 1 auto;margin-right:10px;box-sizing:border-box;height:var(--line-height);line-height:var(--line-height)}.ui-popup-mask .ui-popup-container .ui-popup-body .setting-wrapper>.setting-item>.setting-label+textarea{text-indent:var(--text-indent)}.ui-popup-mask .ui-popup-container .ui-popup-body .setting-wrapper>.setting-item>.setting-label+.ui-check-wrapper{padding:0}.ui-popup-mask .ui-popup-container .ui-popup-footer{flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;padding:4px 10px 16px 2px}.ui-popup-mask .ui-popup-container .ui-popup-footer .ui-popup-button{margin-left:12px;border:none;line-height:28px;color:var(--title-color);border-radius:var(--corner-radius);padding:4px 16px;box-sizing:border-box;min-width:70px;text-align:center;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:var(--title-ctrlbg-color);transition:opacity .12s ease}.ui-popup-mask .ui-popup-container .ui-popup-footer .ui-popup-button:focus,.ui-popup-mask .ui-popup-container .ui-popup-footer .ui-popup-button:hover{opacity:.8}.ui-popup-mask .ui-popup-container .ui-popup-footer .ui-popup-button:focus,.ui-popup-mask .ui-popup-container .ui-popup-footer .ui-popup-button:focus-visible{outline:none}.ui-popup-mask .ui-popup-container .ui-popup-border{position:absolute}.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-left,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-right{width:6px;height:100%;top:0;cursor:ew-resize}.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom{width:100%;height:6px;left:0;cursor:ns-resize}.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top-left,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top-right,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom-right,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom-left{width:8px;height:8px}.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top-left,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom-right{cursor:nwse-resize}.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top-right,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom-left{cursor:nesw-resize}.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-left,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top-left,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom-left{left:-4px}.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-right,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top-right,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom-right{right:-4px}.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top-left,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-top-right{top:-4px}.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom-right,.ui-popup-mask .ui-popup-container .ui-popup-border.ui-popup-border-bottom-left{bottom:-4px}.ui-popup-mask .ui-popup-container.ui-popup-collapse{min-height:40px;min-width:160px}.ui-popup-mask .ui-popup-container.ui-popup-collapse .ui-popup-body,.ui-popup-mask .ui-popup-container.ui-popup-collapse .ui-popup-footer,.ui-popup-mask .ui-popup-container.ui-popup-collapse .ui-popup-border{display:none}.ui-tooltip-color{background-color:#fff;color:#323130;border-color:rgba(204,204,204,.8)}.ui-tooltip-color:focus,.ui-tooltip-color:focus-visible{outline:none}.ui-tooltip-wrapper{position:absolute;word-wrap:break-word;height:auto;text-align:left;z-index:250;min-width:45px;max-width:480px;min-height:32px;border-radius:2px;box-shadow:0 3.2px 7.2px 0 rgba(0,0,0,.13),0 .6px 1.8px 0 rgba(0,0,0,.11);transition:visibility 0s linear 120ms,opacity 120ms ease}.ui-tooltip-wrapper>.ui-tooltip-pointer{box-sizing:border-box;box-shadow:0 5px 15px 2px rgba(0,0,0,.3);border:1px solid #fff;z-index:-1;width:16px;height:16px;position:absolute;left:calc(50% - 8px);bottom:-8px;transform:rotate(-45deg);transform-origin:center}.ui-tooltip-wrapper>.ui-tooltip-curtain{position:absolute;width:100%;height:100%;z-index:-1}.ui-tooltip-wrapper>.ui-tooltip-content{font-size:var(--font-smaller-size);line-height:1rem;white-space:normal;overflow:hidden;margin:8px;height:calc(100% - 16px);-webkit-user-select:none;-moz-user-select:none;user-select:none}.ui-tooltip-wrapper.ui-tooltip-down>.ui-tooltip-pointer{bottom:unset;top:-8px}.ui-tooltip-wrapper.ui-tooltip-no>.ui-tooltip-pointer{display:none}:root{font-family:var(--serif-font-family);font-size:1.125rem;line-height:1.5;font-weight:400;color-scheme:light dark;color:var(--color);background-color:var(--bg-color);font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%;--split-border-color: #ccc;--hover-color: #666;--serif-font-family: "Segoe UI Variable Display", Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;--mono-font-family: "FantasqueSansMono NFM", "Cascadia Code", "PT Mono", Consolas, "Courier New", monospace}input{font-family:var(--serif-font-family)}code,kbd,pre,samp{font-family:var(--mono-font-family);background-color:var(--hover-color);padding:0 10px}code{display:inline-block}pre,samp{font-size:.875em}h2+code{margin-left:70px;position:relative}h2+code::before{content:"签名:";position:absolute;margin-left:-70px}h3,h4{font-family:var(--mono-font-family);font-size:1em;margin-left:10px}h3~p{margin-left:10px}h4{font-size:.9em;-webkit-margin-after:.4em;margin-block-end:.4em}h4+code{font-size:.9rem}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}button{border-radius:8px;border:1px solid rgba(0,0,0,0);padding:.6em 1.2em;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}body{margin:0;display:flex;height:100vh}h1{font-size:3.2em;line-height:1.1;margin:20px 0}#directory{width:200px;padding:2rem;border-right:1px solid var(--split-border-color);flex:0 0 auto}#directory>ul{padding:0;line-height:1.6em}#directory>ul>li{list-style:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}#directory>ul>li.title{margin:20px 0 6px;font-weight:bold;font-size:1.25em}#directory ol{padding-left:10px}#directory ol>li{padding:0 6px;list-style-position:inside;cursor:pointer}#directory ol>li:hover{background-color:var(--hover-color)}#container{width:600px;overflow:auto;padding:20px}#container>div{margin-right:20px}.app-module{margin:8px 0}#create-icon{display:flex;justify-content:center}#create-icon svg{width:20px;height:20px}#create-checkbox{display:flex;flex-direction:column;align-items:center}.ui-check-wrapper .ui-check-inner{width:14px;height:14px}.ui-check-wrapper>span{font-size:1em}.icon-col{cursor:pointer;color:#7b1c21;display:block;overflow:hidden;text-overflow:ellipsis;white-space:pre;text-align:center}.icon-col:hover{text-decoration:underline}@media(prefers-color-scheme: light){:root{--split-border-color: #666;--hover-color: #eee}a:hover{color:#747bff}button{background-color:#f9f9f9}} \ No newline at end of file diff --git a/style.scss b/style.scss index 8cf5f33..7352f7b 100644 --- a/style.scss +++ b/style.scss @@ -163,7 +163,8 @@ h1 { } #container { - flex: 1 1 auto; + // flex: 1 1 auto; + width: 600px; overflow: auto; padding: 20px; diff --git a/vite.build.js b/vite.build.js index bfdba58..3a70c0a 100644 --- a/vite.build.js +++ b/vite.build.js @@ -56,6 +56,36 @@ const libraries = [ }) ] }, + { + clearScreen: false, + css: { + postcss: { + plugins: [postcssPresetEnv()] + } + }, + build: { + lib: { + entry: './lib/element.js', + name: 'lib-element', + formats: ['umd'], + fileName: (_format, name) => `${name}.min.js` + }, + rollupOptions: { + output: { + assetFileNames: info => info.name === 'style.css' ? 'element.min.css' : info.name + } + }, + sourcemap: true, + outDir: '../../../../IronIntel/Contractor2.0/Contractor/Site/js/lib', + emptyOutDir: false + }, + plugins: [ + viteExternalsPlugin({ + '../ui': 'lib-ui', + '../utility': 'lib-utility' + }) + ] + }, { clearScreen: false, build: {