@cyberskill/design 1.1.1 → 1.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +28 -5
- package/README.md +23 -22
- package/SKILL.md +7 -7
- package/THIRD-PARTY-NOTICES.md +35 -0
- package/VERSION +1 -1
- package/_ds_bundle.js +1495 -1733
- package/_ds_manifest.json +1 -1
- package/_esm/cs.mjs +18 -4
- package/_esm/react.d.ts +2 -0
- package/_esm/react.mjs +7 -1
- package/_vendor/NOTICE.md +16 -0
- package/_vendor/babel/README.md +7 -0
- package/_vendor/babel/babel.min.js +4 -0
- package/_vendor/react/README.md +3 -0
- package/_vendor/react/react-dom.production.min.js +1 -0
- package/_vendor/react/react.production.min.js +1 -0
- package/assets/aurora-hoa.webp +0 -0
- package/assets/aurora-kim.webp +0 -0
- package/assets/aurora-moc.webp +0 -0
- package/assets/aurora-tho.webp +0 -0
- package/assets/aurora-thuy.webp +0 -0
- package/base/a11y.css +18 -0
- package/base/ai.css +9 -4
- package/base/collections.css +18 -7
- package/base/components.css +16 -4
- package/base/controls.css +116 -66
- package/base/data.css +4 -4
- package/base/feedback.css +14 -2
- package/base/forms.css +10 -4
- package/base/glass.css +7 -1
- package/base/interaction.css +6 -2
- package/base/navigation.css +9 -3
- package/base/reset.css +7 -1
- package/base/responsive.css +38 -12
- package/base/shell.css +15 -3
- package/components/_i18n/i18n.js +41 -4
- package/components/_i18n/strings.js +89 -22
- package/components/ai/ChatMessage.d.ts +2 -0
- package/components/ai/ChatMessage.jsx +8 -4
- package/components/ai/ConfidenceMeter.d.ts +2 -0
- package/components/ai/ConfidenceMeter.jsx +8 -6
- package/components/ai/HumanReviewGate.d.ts +2 -0
- package/components/ai/PromptInput.d.ts +2 -0
- package/components/ai/TypingIndicator.d.ts +2 -0
- package/components/ai/ai-conversation.card.html +3 -3
- package/components/ai/ai-lumi.card.html +3 -3
- package/components/ai/ai.card.html +3 -3
- package/components/brand/brand.card.html +3 -3
- package/components/button/buttons.card.html +3 -3
- package/components/data/Image.jsx +18 -2
- package/components/data/Sortable.d.ts +5 -1
- package/components/data/Sortable.jsx +42 -9
- package/components/data/Sortable.prompt.md +4 -2
- package/components/data/Terminal.jsx +3 -2
- package/components/data/Tooltip.d.ts +2 -0
- package/components/data/Tooltip.jsx +6 -3
- package/components/data/Tree.d.ts +2 -1
- package/components/data/Tree.jsx +137 -16
- package/components/data/Tree.prompt.md +1 -1
- package/components/data/Watermark.jsx +11 -3
- package/components/data/collections.card.html +3 -3
- package/components/data/data.card.html +3 -3
- package/components/datatable/DataGrid.jsx +10 -1
- package/components/datatable/DataTable.d.ts +24 -1
- package/components/datatable/DataTable.jsx +78 -5
- package/components/datatable/DataTable.prompt.md +2 -1
- package/components/datatable/datatable.card.html +20 -16
- package/components/dialog/Dialog.jsx +13 -45
- package/components/dialog/dialog.card.html +3 -3
- package/components/feedback/Alert.d.ts +2 -0
- package/components/feedback/Alert.jsx +8 -2
- package/components/feedback/EmptyState.d.ts +2 -0
- package/components/feedback/EmptyState.jsx +6 -3
- package/components/feedback/ProgressBar.d.ts +2 -0
- package/components/feedback/ProgressBar.jsx +9 -4
- package/components/feedback/Toast.jsx +15 -1
- package/components/feedback/Toast.prompt.md +2 -1
- package/components/feedback/feedback-2.card.html +3 -3
- package/components/feedback/feedback.card.html +3 -3
- package/components/forms/Cascader.jsx +76 -8
- package/components/forms/Editor.jsx +37 -2
- package/components/forms/FileUpload.d.ts +2 -0
- package/components/forms/FileUpload.jsx +22 -8
- package/components/forms/Form.d.ts +3 -1
- package/components/forms/Form.jsx +9 -1
- package/components/forms/Form.prompt.md +1 -1
- package/components/forms/Mentions.d.ts +1 -1
- package/components/forms/Mentions.jsx +59 -7
- package/components/forms/Mentions.prompt.md +3 -1
- package/components/forms/Rating.d.ts +3 -2
- package/components/forms/Rating.jsx +34 -3
- package/components/forms/Rating.prompt.md +1 -1
- package/components/forms/Select.d.ts +2 -0
- package/components/forms/Select.jsx +6 -2
- package/components/forms/Slider.d.ts +2 -0
- package/components/forms/Slider.jsx +5 -2
- package/components/forms/Switch.d.ts +2 -0
- package/components/forms/Switch.jsx +15 -3
- package/components/forms/TimePicker.jsx +3 -1
- package/components/forms/Toggle.d.ts +2 -0
- package/components/forms/Toggle.jsx +14 -2
- package/components/forms/forms-2.card.html +3 -3
- package/components/forms/forms.card.html +3 -3
- package/components/icon/Icon.d.ts +1 -1
- package/components/icon/Icon.jsx +1 -0
- package/components/icon/Icon.prompt.md +1 -1
- package/components/icon/icon.card.html +3 -3
- package/components/logo/logo.card.html +3 -3
- package/components/navigation/CommandPalette.d.ts +2 -0
- package/components/navigation/CommandPalette.jsx +22 -7
- package/components/navigation/Menu.d.ts +2 -0
- package/components/navigation/Menu.jsx +60 -6
- package/components/navigation/Steps.d.ts +2 -0
- package/components/navigation/Steps.jsx +6 -3
- package/components/navigation/Tabs.d.ts +2 -0
- package/components/navigation/Tabs.jsx +6 -3
- package/components/navigation/Toolbar.d.ts +3 -2
- package/components/navigation/Toolbar.jsx +119 -12
- package/components/navigation/Toolbar.prompt.md +1 -1
- package/components/navigation/navigation.card.html +3 -3
- package/components/navigation/shell.card.html +3 -3
- package/components/overlays/AlertDialog.jsx +19 -49
- package/components/overlays/ContextMenu.d.ts +2 -0
- package/components/overlays/ContextMenu.jsx +6 -3
- package/components/overlays/Drawer.d.ts +2 -0
- package/components/overlays/Drawer.jsx +24 -20
- package/components/overlays/OverlayManager.d.ts +40 -0
- package/components/overlays/OverlayManager.jsx +214 -0
- package/components/overlays/OverlayManager.prompt.md +12 -0
- package/components/overlays/Tour.jsx +14 -7
- package/components/overlays/alertdialog.card.html +3 -3
- package/components/overlays/overlays.card.html +3 -3
- package/components/textfield/textfield.card.html +3 -3
- package/dist/styles.min.css +2252 -0
- package/docs/benchmark-rubric.md +1 -1
- package/docs/ci-cd.md +19 -12
- package/docs/consumer-grant.md +3 -0
- package/docs/consuming.md +13 -13
- package/docs/contrast-report.md +79 -1
- package/docs/conventions.md +21 -8
- package/docs/decisions.md +45 -7
- package/docs/deploy.md +17 -6
- package/docs/design-styles.md +2 -2
- package/docs/quality-gates.md +35 -15
- package/docs/release-notes.md +2 -2
- package/docs/release-runbook.md +78 -0
- package/docs/sync.md +2 -2
- package/docs/vi/benchmark-rubric.md +1 -1
- package/docs/vi/ci-cd.md +17 -12
- package/docs/vi/consumer-grant.md +3 -0
- package/docs/vi/consuming.md +13 -13
- package/docs/vi/contrast-report.md +79 -1
- package/docs/vi/conventions.md +20 -7
- package/docs/vi/decisions.md +43 -6
- package/docs/vi/deploy.md +17 -6
- package/docs/vi/quality-gates.md +34 -15
- package/docs/vi/release-notes.md +2 -2
- package/docs/vi/release-runbook.md +78 -0
- package/docs/vi/sync.md +2 -2
- package/docs/viewer.html +2 -2
- package/fonts/OFL.txt +98 -0
- package/guidelines/atomic-view.html +13 -9
- package/guidelines/brand-imagery.html +1 -1
- package/guidelines/elements-variants.html +5 -5
- package/guidelines/spacing-breakpoints.html +2 -1
- package/guidelines/surfaces-glass.html +1 -1
- package/llms.txt +5 -5
- package/package.json +38 -10
- package/templates/_ds-base.canonical.js +55 -0
- package/templates/ai-cluster-demo.html +2 -2
- package/templates/app-shell/AppShell.dc.html +7 -5
- package/templates/app-shell/content-schema.json +7 -0
- package/templates/app-shell/ds-base.js +33 -1
- package/templates/app-shell/support.js +13 -20
- package/templates/article/Article.dc.html +7 -3
- package/templates/article/content-schema.json +7 -0
- package/templates/article/ds-base.js +33 -1
- package/templates/article/support.js +13 -20
- package/templates/auth/Auth.dc.html +33 -12
- package/templates/auth/content-schema.json +37 -0
- package/templates/auth/ds-base.js +33 -1
- package/templates/auth/support.js +13 -20
- package/templates/bod-deck/BodDeck.dc.html +11 -10
- package/templates/bod-deck/ds-base.js +33 -1
- package/templates/bod-deck/support.js +13 -20
- package/templates/bod-investor-update/BodInvestorUpdate.dc.html +4 -3
- package/templates/bod-investor-update/ds-base.js +33 -1
- package/templates/bod-investor-update/support.js +13 -20
- package/templates/bod-memo/BodMemo.dc.html +6 -5
- package/templates/bod-memo/ds-base.js +33 -1
- package/templates/bod-memo/support.js +13 -20
- package/templates/bod-minutes/BodMinutes.dc.html +2 -1
- package/templates/bod-minutes/ds-base.js +33 -1
- package/templates/bod-minutes/support.js +13 -20
- package/templates/bod-report/BodReport.dc.html +3 -2
- package/templates/bod-report/ds-base.js +33 -1
- package/templates/bod-report/support.js +13 -20
- package/templates/bod-resolution/BodResolution.dc.html +5 -4
- package/templates/bod-resolution/ds-base.js +33 -1
- package/templates/bod-resolution/support.js +13 -20
- package/templates/culture-event-invite/CultureEventInvite.dc.html +3 -2
- package/templates/culture-event-invite/ds-base.js +33 -1
- package/templates/culture-event-invite/support.js +13 -20
- package/templates/dashboard/Dashboard.dc.html +10 -6
- package/templates/dashboard/content-schema.json +7 -0
- package/templates/dashboard/ds-base.js +33 -1
- package/templates/dashboard/support.js +13 -20
- package/templates/delivery-change-note/DeliveryChangeNote.dc.html +4 -3
- package/templates/delivery-change-note/ds-base.js +33 -1
- package/templates/delivery-change-note/support.js +13 -20
- package/templates/delivery-kickoff/DeliveryKickoff.dc.html +3 -2
- package/templates/delivery-kickoff/ds-base.js +33 -1
- package/templates/delivery-kickoff/support.js +13 -20
- package/templates/delivery-qbr-deck/DeliveryQbrDeck.dc.html +6 -5
- package/templates/delivery-qbr-deck/ds-base.js +33 -1
- package/templates/delivery-qbr-deck/support.js +13 -20
- package/templates/delivery-runbook/DeliveryRunbook.dc.html +5 -4
- package/templates/delivery-runbook/ds-base.js +33 -1
- package/templates/delivery-runbook/support.js +13 -20
- package/templates/delivery-status-email/DeliveryStatusEmail.dc.html +3 -2
- package/templates/delivery-status-email/ds-base.js +33 -1
- package/templates/delivery-status-email/support.js +13 -20
- package/templates/doc-style-guide/DocStyleGuide.dc.html +67 -64
- package/templates/doc-style-guide/ds-base.js +33 -1
- package/templates/doc-style-guide/support.js +13 -20
- package/templates/doc-suite-index/DocSuiteIndex.dc.html +46 -45
- package/templates/doc-suite-index/ds-base.js +33 -1
- package/templates/doc-suite-index/support.js +13 -20
- package/templates/doc-templates/DocTemplates.dc.html +42 -41
- package/templates/doc-templates/ds-base.js +33 -1
- package/templates/doc-templates/support.js +13 -20
- package/templates/email/Email.dc.html +3 -2
- package/templates/email/ds-base.js +33 -1
- package/templates/email/support.js +13 -20
- package/templates/email-safe/email-announcement.html +2 -2
- package/templates/email-safe/email-dunning.html +2 -2
- package/templates/email-safe/email-investor.html +2 -2
- package/templates/email-safe/email-launch.html +2 -2
- package/templates/email-safe/email-newsletter.html +2 -2
- package/templates/email-safe/email-status.html +2 -2
- package/templates/email-safe/email-transactional.html +2 -2
- package/templates/finance-dunning-email/FinanceDunningEmail.dc.html +6 -5
- package/templates/finance-dunning-email/ds-base.js +33 -1
- package/templates/finance-dunning-email/support.js +13 -20
- package/templates/finance-expense-report/FinanceExpenseReport.dc.html +4 -3
- package/templates/finance-expense-report/ds-base.js +33 -1
- package/templates/finance-expense-report/support.js +13 -20
- package/templates/finance-invoice/FinanceInvoice.dc.html +6 -5
- package/templates/finance-invoice/ds-base.js +33 -1
- package/templates/finance-invoice/support.js +13 -20
- package/templates/finance-quote/FinanceQuote.dc.html +6 -5
- package/templates/finance-quote/ds-base.js +33 -1
- package/templates/finance-quote/support.js +13 -20
- package/templates/hr-announcement/HrAnnouncement.dc.html +5 -4
- package/templates/hr-announcement/ds-base.js +33 -1
- package/templates/hr-announcement/support.js +13 -20
- package/templates/hr-interview-kit/HrInterviewKit.dc.html +5 -4
- package/templates/hr-interview-kit/ds-base.js +33 -1
- package/templates/hr-interview-kit/support.js +13 -20
- package/templates/hr-offboarding/HrOffboarding.dc.html +2 -1
- package/templates/hr-offboarding/ds-base.js +33 -1
- package/templates/hr-offboarding/support.js +13 -20
- package/templates/hr-performance-review/HrPerformanceReview.dc.html +2 -1
- package/templates/hr-performance-review/ds-base.js +33 -1
- package/templates/hr-performance-review/support.js +13 -20
- package/templates/hr-pip/HrPip.dc.html +2 -1
- package/templates/hr-pip/ds-base.js +33 -1
- package/templates/hr-pip/support.js +13 -20
- package/templates/kitchen-sink.html +9 -4
- package/templates/legal-msa/LegalMsa.dc.html +2 -1
- package/templates/legal-msa/ds-base.js +33 -1
- package/templates/legal-msa/support.js +13 -20
- package/templates/legal-sow/LegalSow.dc.html +2 -1
- package/templates/legal-sow/ds-base.js +33 -1
- package/templates/legal-sow/support.js +13 -20
- package/templates/marketing-campaign-brief/MarketingCampaignBrief.dc.html +3 -2
- package/templates/marketing-campaign-brief/ds-base.js +33 -1
- package/templates/marketing-campaign-brief/support.js +13 -20
- package/templates/marketing-case-study/MarketingCaseStudy.dc.html +6 -5
- package/templates/marketing-case-study/ds-base.js +33 -1
- package/templates/marketing-case-study/support.js +13 -20
- package/templates/marketing-launch/MarketingLaunch.dc.html +6 -5
- package/templates/marketing-launch/ds-base.js +33 -1
- package/templates/marketing-launch/support.js +13 -20
- package/templates/marketing-newsletter/MarketingNewsletter.dc.html +13 -12
- package/templates/marketing-newsletter/ds-base.js +33 -1
- package/templates/marketing-newsletter/support.js +13 -20
- package/templates/marketing-page/MarketingPage.dc.html +19 -14
- package/templates/marketing-page/content-schema.json +1 -0
- package/templates/marketing-page/ds-base.js +33 -1
- package/templates/marketing-page/support.js +13 -20
- package/templates/marketing-press-release/MarketingPressRelease.dc.html +2 -1
- package/templates/marketing-press-release/ds-base.js +33 -1
- package/templates/marketing-press-release/support.js +13 -20
- package/templates/marketing-social-kit/MarketingSocialKit.dc.html +5 -4
- package/templates/marketing-social-kit/ds-base.js +33 -1
- package/templates/marketing-social-kit/support.js +13 -20
- package/templates/sales-one-pager/SalesOnePager.dc.html +5 -4
- package/templates/sales-one-pager/ds-base.js +33 -1
- package/templates/sales-one-pager/support.js +13 -20
- package/templates/sales-proposal-deck/SalesProposalDeck.dc.html +7 -6
- package/templates/sales-proposal-deck/ds-base.js +33 -1
- package/templates/sales-proposal-deck/support.js +13 -20
- package/templates/settings/Settings.dc.html +7 -3
- package/templates/settings/content-schema.json +7 -0
- package/templates/settings/ds-base.js +33 -1
- package/templates/settings/support.js +13 -20
- package/templates/slide-deck/SlideDeck.dc.html +5 -4
- package/templates/slide-deck/ds-base.js +33 -1
- package/templates/slide-deck/support.js +13 -20
- package/templates/team-meeting-agenda/TeamMeetingAgenda.dc.html +2 -1
- package/templates/team-meeting-agenda/ds-base.js +33 -1
- package/templates/team-meeting-agenda/support.js +13 -20
- package/templates/tech-incident-report/TechIncidentReport.dc.html +3 -2
- package/templates/tech-incident-report/ds-base.js +33 -1
- package/templates/tech-incident-report/support.js +13 -20
- package/templates/tech-release-notes/TechReleaseNotes.dc.html +6 -5
- package/templates/tech-release-notes/ds-base.js +33 -1
- package/templates/tech-release-notes/support.js +13 -20
- package/templates/tech-retro/TechRetro.dc.html +4 -3
- package/templates/tech-retro/ds-base.js +33 -1
- package/templates/tech-retro/support.js +13 -20
- package/templates/tech-rfc/TechRfc.dc.html +3 -2
- package/templates/tech-rfc/ds-base.js +33 -1
- package/templates/tech-rfc/support.js +13 -20
- package/templates/vn-appointment-transfer/VnAppointmentTransfer.dc.html +36 -35
- package/templates/vn-appointment-transfer/ds-base.js +33 -1
- package/templates/vn-appointment-transfer/support.js +13 -20
- package/templates/vn-certificate-of-employment/VnCertificateOfEmployment.dc.html +27 -26
- package/templates/vn-certificate-of-employment/ds-base.js +33 -1
- package/templates/vn-certificate-of-employment/support.js +13 -20
- package/templates/vn-code-of-conduct/VnCodeOfConduct.dc.html +44 -43
- package/templates/vn-code-of-conduct/ds-base.js +33 -1
- package/templates/vn-code-of-conduct/support.js +13 -20
- package/templates/vn-compensation-regulation/VnCompensationRegulation.dc.html +53 -52
- package/templates/vn-compensation-regulation/ds-base.js +33 -1
- package/templates/vn-compensation-regulation/support.js +13 -20
- package/templates/vn-contract-amendment/VnContractAmendment.dc.html +41 -40
- package/templates/vn-contract-amendment/ds-base.js +33 -1
- package/templates/vn-contract-amendment/support.js +13 -20
- package/templates/vn-contractor-agreement/VnContractorAgreement.dc.html +50 -49
- package/templates/vn-contractor-agreement/ds-base.js +33 -1
- package/templates/vn-contractor-agreement/support.js +13 -20
- package/templates/vn-data-protection-policy/VnDataProtectionPolicy.dc.html +54 -53
- package/templates/vn-data-protection-policy/ds-base.js +33 -1
- package/templates/vn-data-protection-policy/support.js +13 -20
- package/templates/vn-disciplinary-case-file/VnDisciplinaryCaseFile.dc.html +72 -71
- package/templates/vn-disciplinary-case-file/ds-base.js +33 -1
- package/templates/vn-disciplinary-case-file/support.js +13 -20
- package/templates/vn-disciplinary-schedule/VnDisciplinarySchedule.dc.html +21 -20
- package/templates/vn-disciplinary-schedule/ds-base.js +33 -1
- package/templates/vn-disciplinary-schedule/support.js +13 -20
- package/templates/vn-employee-request/VnEmployeeRequest.dc.html +54 -53
- package/templates/vn-employee-request/ds-base.js +33 -1
- package/templates/vn-employee-request/support.js +13 -20
- package/templates/vn-exit-handover/VnExitHandover.dc.html +122 -121
- package/templates/vn-exit-handover/ds-base.js +33 -1
- package/templates/vn-exit-handover/support.js +13 -20
- package/templates/vn-foreign-employee-pack/VnForeignEmployeePack.dc.html +31 -30
- package/templates/vn-foreign-employee-pack/ds-base.js +33 -1
- package/templates/vn-foreign-employee-pack/support.js +13 -20
- package/templates/vn-grassroots-democracy/VnGrassrootsDemocracy.dc.html +54 -53
- package/templates/vn-grassroots-democracy/ds-base.js +33 -1
- package/templates/vn-grassroots-democracy/support.js +13 -20
- package/templates/vn-infosec-policy/VnInfosecPolicy.dc.html +50 -49
- package/templates/vn-infosec-policy/ds-base.js +33 -1
- package/templates/vn-infosec-policy/support.js +13 -20
- package/templates/vn-internal-labor-regulations/VnInternalLaborRegulations.dc.html +84 -83
- package/templates/vn-internal-labor-regulations/ds-base.js +33 -1
- package/templates/vn-internal-labor-regulations/support.js +13 -20
- package/templates/vn-internship-agreement/VnInternshipAgreement.dc.html +44 -43
- package/templates/vn-internship-agreement/ds-base.js +33 -1
- package/templates/vn-internship-agreement/support.js +13 -20
- package/templates/vn-job-description/VnJobDescription.dc.html +28 -27
- package/templates/vn-job-description/ds-base.js +33 -1
- package/templates/vn-job-description/support.js +13 -20
- package/templates/vn-labor-contract/VnLaborContract.dc.html +109 -108
- package/templates/vn-labor-contract/ds-base.js +33 -1
- package/templates/vn-labor-contract/support.js +13 -20
- package/templates/vn-labour-management-book/VnLabourManagementBook.dc.html +32 -31
- package/templates/vn-labour-management-book/ds-base.js +33 -1
- package/templates/vn-labour-management-book/support.js +13 -20
- package/templates/vn-mutual-nda/VnMutualNda.dc.html +45 -44
- package/templates/vn-mutual-nda/ds-base.js +33 -1
- package/templates/vn-mutual-nda/support.js +13 -20
- package/templates/vn-ndnca-ip/VnNdncaIp.dc.html +73 -72
- package/templates/vn-ndnca-ip/ds-base.js +33 -1
- package/templates/vn-ndnca-ip/support.js +13 -20
- package/templates/vn-offer-letter/VnOfferLetter.dc.html +32 -31
- package/templates/vn-offer-letter/ds-base.js +33 -1
- package/templates/vn-offer-letter/support.js +13 -20
- package/templates/vn-onboarding-checklist/VnOnboardingChecklist.dc.html +16 -15
- package/templates/vn-onboarding-checklist/ds-base.js +33 -1
- package/templates/vn-onboarding-checklist/support.js +13 -20
- package/templates/vn-pdpl-consent/VnPdplConsent.dc.html +43 -42
- package/templates/vn-pdpl-consent/ds-base.js +33 -1
- package/templates/vn-pdpl-consent/support.js +13 -20
- package/templates/vn-performance-evaluation-regulation/VnPerformanceEvaluationRegulation.dc.html +65 -64
- package/templates/vn-performance-evaluation-regulation/ds-base.js +33 -1
- package/templates/vn-performance-evaluation-regulation/support.js +13 -20
- package/templates/vn-performance-review-form/VnPerformanceReviewForm.dc.html +49 -48
- package/templates/vn-performance-review-form/ds-base.js +33 -1
- package/templates/vn-performance-review-form/support.js +13 -20
- package/templates/vn-phantom-stock/VnPhantomStock.dc.html +41 -40
- package/templates/vn-phantom-stock/ds-base.js +33 -1
- package/templates/vn-phantom-stock/support.js +13 -20
- package/templates/vn-probation-contract/VnProbationContract.dc.html +56 -55
- package/templates/vn-probation-contract/ds-base.js +33 -1
- package/templates/vn-probation-contract/support.js +13 -20
- package/templates/vn-receipt-acknowledgement/VnReceiptAcknowledgement.dc.html +28 -27
- package/templates/vn-receipt-acknowledgement/ds-base.js +33 -1
- package/templates/vn-receipt-acknowledgement/support.js +13 -20
- package/templates/vn-remote-work-policy/VnRemoteWorkPolicy.dc.html +44 -43
- package/templates/vn-remote-work-policy/ds-base.js +33 -1
- package/templates/vn-remote-work-policy/support.js +13 -20
- package/templates/vn-resignation-letter/VnResignationLetter.dc.html +20 -19
- package/templates/vn-resignation-letter/ds-base.js +33 -1
- package/templates/vn-resignation-letter/support.js +13 -20
- package/templates/vn-salary-scale/VnSalaryScale.dc.html +39 -38
- package/templates/vn-salary-scale/ds-base.js +33 -1
- package/templates/vn-salary-scale/support.js +13 -20
- package/templates/vn-sow-appendix/VnSowAppendix.dc.html +21 -20
- package/templates/vn-sow-appendix/ds-base.js +33 -1
- package/templates/vn-sow-appendix/support.js +13 -20
- package/templates/vn-termination-decision/VnTerminationDecision.dc.html +25 -24
- package/templates/vn-termination-decision/ds-base.js +33 -1
- package/templates/vn-termination-decision/support.js +13 -20
- package/templates/vn-total-rewards-appendix/VnTotalRewardsAppendix.dc.html +63 -62
- package/templates/vn-total-rewards-appendix/ds-base.js +33 -1
- package/templates/vn-total-rewards-appendix/support.js +13 -20
- package/templates/vn-training-commitment/VnTrainingCommitment.dc.html +39 -38
- package/templates/vn-training-commitment/ds-base.js +33 -1
- package/templates/vn-training-commitment/support.js +13 -20
- package/templates/vn-travel-expense-policy/VnTravelExpensePolicy.dc.html +50 -49
- package/templates/vn-travel-expense-policy/ds-base.js +33 -1
- package/templates/vn-travel-expense-policy/support.js +13 -20
- package/tokens/colors.css +19 -1
- package/tokens/component-tokens.css +1 -0
- package/tokens/element-seeds.json +2 -2
- package/tokens/elements.css +228 -21
- package/tokens/elevation.css +8 -2
- package/tokens/native/CSTokens.kt +37 -2
- package/tokens/native/CSTokens.swift +37 -2
- package/tokens/native/cs_tokens.dart +37 -2
- package/tokens/provenance.json +12 -12
- package/tokens/spacing.css +4 -1
- package/tokens/tokens.dtcg.json +129 -16
- package/tokens/tokens.js +59 -18
- package/tokens/tokens.json +58 -17
- package/tokens/typography.css +2 -0
- package/ui_kits/deck/index.html +4 -0
- package/ui_kits/status-hub/StatusHub.jsx +30 -4
- package/ui_kits/status-hub/index.html +3 -3
- package/ui_kits/status-hub/login.html +55 -9
- package/ui_kits/status-hub/project.html +4 -0
- package/ui_kits/status-hub/settings.html +35 -13
- package/ui_kits/status-hub/status.css +5 -1
- package/ui_kits/website/Website.jsx +25 -3
- package/ui_kits/website/careers.html +5 -1
- package/ui_kits/website/chat.html +5 -3
- package/ui_kits/website/index.html +3 -3
- package/ui_kits/website/site.css +3 -3
- package/ui_kits/website/work.html +5 -1
- package/assets/aurora-hoa.png +0 -0
- package/assets/aurora-kim.png +0 -0
- package/assets/aurora-moc.png +0 -0
- package/assets/aurora-thuy.png +0 -0
- package/docs/status/assets/favicon.svg +0 -5
- package/docs/status/assets/status-legacy.css +0 -399
- package/docs/status/assets/status-legacy.js +0 -505
- package/docs/status/assets/status.css +0 -644
- package/docs/status/assets/status.js +0 -1308
- package/docs/status/data/status-feed.json +0 -1
- package/docs/status/data/task/TASK-REL-001.js +0 -1
- package/docs/status/data/task/TASK-REL-002.js +0 -1
- package/docs/status/index.html +0 -42
- package/docs/status/roadmap.html +0 -4
- package/docs/status/status-legacy.html +0 -117
- package/docs/tasks/BACKLOG.md +0 -26
- package/docs/tasks/release/TASK-REL-001-launch-1-1-0/spec.md +0 -55
- package/docs/tasks/release/TASK-REL-002-republish-1-1-1/spec.md +0 -54
package/_ds_bundle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* @ds-bundle: {"format":4,"namespace":"CyberSkillDesignSystem_847b3c","components":[{"name":"AIDisclosureBadge","sourcePath":"components/ai/AIDisclosureBadge.jsx"},{"name":"ChatMessage","sourcePath":"components/ai/ChatMessage.jsx"},{"name":"CitationList","sourcePath":"components/ai/CitationList.jsx"},{"name":"ConfidenceMeter","sourcePath":"components/ai/ConfidenceMeter.jsx"},{"name":"HumanReviewGate","sourcePath":"components/ai/HumanReviewGate.jsx"},{"name":"PromptInput","sourcePath":"components/ai/PromptInput.jsx"},{"name":"PromptSuggestions","sourcePath":"components/ai/PromptSuggestions.jsx"},{"name":"TypingIndicator","sourcePath":"components/ai/TypingIndicator.jsx"},{"name":"LumiAvatar","sourcePath":"components/brand/LumiAvatar.jsx"},{"name":"Button","sourcePath":"components/button/Button.jsx"},{"name":"ButtonGroup","sourcePath":"components/button/ButtonGroup.jsx"},{"name":"FloatingActionButton","sourcePath":"components/button/FloatingActionButton.jsx"},{"name":"Accordion","sourcePath":"components/data/Accordion.jsx"},{"name":"AspectRatio","sourcePath":"components/data/AspectRatio.jsx"},{"name":"Avatar","sourcePath":"components/data/Avatar.jsx"},{"name":"AvatarGroup","sourcePath":"components/data/Avatar.jsx"},{"name":"Card","sourcePath":"components/data/Card.jsx"},{"name":"CardHeader","sourcePath":"components/data/Card.jsx"},{"name":"CardBody","sourcePath":"components/data/Card.jsx"},{"name":"CardFooter","sourcePath":"components/data/Card.jsx"},{"name":"Carousel","sourcePath":"components/data/Carousel.jsx"},{"name":"Chart","sourcePath":"components/data/Chart.jsx"},{"name":"CodeBlock","sourcePath":"components/data/CodeBlock.jsx"},{"name":"Collapsible","sourcePath":"components/data/Collapsible.jsx"},{"name":"Comment","sourcePath":"components/data/Comment.jsx"},{"name":"DescriptionList","sourcePath":"components/data/DescriptionList.jsx"},{"name":"Divider","sourcePath":"components/data/Divider.jsx"},{"name":"Image","sourcePath":"components/data/Image.jsx"},{"name":"Item","sourcePath":"components/data/Item.jsx"},{"name":"Kbd","sourcePath":"components/data/Kbd.jsx"},{"name":"List","sourcePath":"components/data/List.jsx"},{"name":"ListItem","sourcePath":"components/data/List.jsx"},{"name":"Masonry","sourcePath":"components/data/Masonry.jsx"},{"name":"QRCode","sourcePath":"components/data/QRCode.jsx"},{"name":"ScrollArea","sourcePath":"components/data/ScrollArea.jsx"},{"name":"Sortable","sourcePath":"components/data/Sortable.jsx"},{"name":"Splitter","sourcePath":"components/data/Splitter.jsx"},{"name":"Stat","sourcePath":"components/data/Stat.jsx"},{"name":"Terminal","sourcePath":"components/data/Terminal.jsx"},{"name":"Timeline","sourcePath":"components/data/Timeline.jsx"},{"name":"Tooltip","sourcePath":"components/data/Tooltip.jsx"},{"name":"Tree","sourcePath":"components/data/Tree.jsx"},{"name":"Watermark","sourcePath":"components/data/Watermark.jsx"},{"name":"DataGrid","sourcePath":"components/datatable/DataGrid.jsx"},{"name":"DataTable","sourcePath":"components/datatable/DataTable.jsx"},{"name":"TreeTable","sourcePath":"components/datatable/TreeTable.jsx"},{"name":"Dialog","sourcePath":"components/dialog/Dialog.jsx"},{"name":"Alert","sourcePath":"components/feedback/Alert.jsx"},{"name":"Badge","sourcePath":"components/feedback/Badge.jsx"},{"name":"EmptyState","sourcePath":"components/feedback/EmptyState.jsx"},{"name":"ProgressBar","sourcePath":"components/feedback/ProgressBar.jsx"},{"name":"Result","sourcePath":"components/feedback/Result.jsx"},{"name":"Skeleton","sourcePath":"components/feedback/Skeleton.jsx"},{"name":"Spinner","sourcePath":"components/feedback/Spinner.jsx"},{"name":"StatusIndicator","sourcePath":"components/feedback/StatusIndicator.jsx"},{"name":"Tag","sourcePath":"components/feedback/Tag.jsx"},{"name":"ToastStack","sourcePath":"components/feedback/Toast.jsx"},{"name":"Toast","sourcePath":"components/feedback/Toast.jsx"},{"name":"Calendar","sourcePath":"components/forms/Calendar.jsx"},{"name":"Cascader","sourcePath":"components/forms/Cascader.jsx"},{"name":"Checkbox","sourcePath":"components/forms/Checkbox.jsx"},{"name":"ColorPicker","sourcePath":"components/forms/ColorPicker.jsx"},{"name":"Combobox","sourcePath":"components/forms/Combobox.jsx"},{"name":"DatePicker","sourcePath":"components/forms/DatePicker.jsx"},{"name":"Editor","sourcePath":"components/forms/Editor.jsx"},{"name":"FileUpload","sourcePath":"components/forms/FileUpload.jsx"},{"name":"Form","sourcePath":"components/forms/Form.jsx"},{"name":"FormField","sourcePath":"components/forms/Form.jsx"},{"name":"FormFieldArray","sourcePath":"components/forms/Form.jsx"},{"name":"FormWizard","sourcePath":"components/forms/Form.jsx"},{"name":"InlineEdit","sourcePath":"components/forms/InlineEdit.jsx"},{"name":"InputGroup","sourcePath":"components/forms/InputGroup.jsx"},{"name":"InputOTP","sourcePath":"components/forms/InputOTP.jsx"},{"name":"Mentions","sourcePath":"components/forms/Mentions.jsx"},{"name":"NativeSelect","sourcePath":"components/forms/NativeSelect.jsx"},{"name":"NumberField","sourcePath":"components/forms/NumberField.jsx"},{"name":"Radio","sourcePath":"components/forms/RadioGroup.jsx"},{"name":"RadioGroup","sourcePath":"components/forms/RadioGroup.jsx"},{"name":"Rating","sourcePath":"components/forms/Rating.jsx"},{"name":"SearchField","sourcePath":"components/forms/SearchField.jsx"},{"name":"SegmentedControl","sourcePath":"components/forms/SegmentedControl.jsx"},{"name":"Select","sourcePath":"components/forms/Select.jsx"},{"name":"Slider","sourcePath":"components/forms/Slider.jsx"},{"name":"Switch","sourcePath":"components/forms/Switch.jsx"},{"name":"TagInput","sourcePath":"components/forms/TagInput.jsx"},{"name":"Textarea","sourcePath":"components/forms/Textarea.jsx"},{"name":"TimePicker","sourcePath":"components/forms/TimePicker.jsx"},{"name":"Toggle","sourcePath":"components/forms/Toggle.jsx"},{"name":"Transfer","sourcePath":"components/forms/Transfer.jsx"},{"name":"TreeSelect","sourcePath":"components/forms/TreeSelect.jsx"},{"name":"CS_ICONS","sourcePath":"components/icon/Icon.jsx"},{"name":"Icon","sourcePath":"components/icon/Icon.jsx"},{"name":"Logo","sourcePath":"components/logo/Logo.jsx"},{"name":"CS_LOGO_VIEWBOX","sourcePath":"components/logo/logo-data.js"},{"name":"CS_LOGO_MARK_INNER","sourcePath":"components/logo/logo-data.js"},{"name":"Anchor","sourcePath":"components/navigation/Anchor.jsx"},{"name":"BackTop","sourcePath":"components/navigation/BackTop.jsx"},{"name":"Breadcrumb","sourcePath":"components/navigation/Breadcrumb.jsx"},{"name":"CommandPalette","sourcePath":"components/navigation/CommandPalette.jsx"},{"name":"Dock","sourcePath":"components/navigation/Dock.jsx"},{"name":"HotKeys","sourcePath":"components/navigation/HotKeys.jsx"},{"name":"Link","sourcePath":"components/navigation/Link.jsx"},{"name":"Menu","sourcePath":"components/navigation/Menu.jsx"},{"name":"MenuItem","sourcePath":"components/navigation/Menu.jsx"},{"name":"Menubar","sourcePath":"components/navigation/Menubar.jsx"},{"name":"NavigationMenu","sourcePath":"components/navigation/NavigationMenu.jsx"},{"name":"Pagination","sourcePath":"components/navigation/Pagination.jsx"},{"name":"Sidebar","sourcePath":"components/navigation/Sidebar.jsx"},{"name":"NavItem","sourcePath":"components/navigation/Sidebar.jsx"},{"name":"Steps","sourcePath":"components/navigation/Steps.jsx"},{"name":"Tabs","sourcePath":"components/navigation/Tabs.jsx"},{"name":"Tab","sourcePath":"components/navigation/Tabs.jsx"},{"name":"Toolbar","sourcePath":"components/navigation/Toolbar.jsx"},{"name":"AlertDialog","sourcePath":"components/overlays/AlertDialog.jsx"},{"name":"ContextMenu","sourcePath":"components/overlays/ContextMenu.jsx"},{"name":"Drawer","sourcePath":"components/overlays/Drawer.jsx"},{"name":"HoverCard","sourcePath":"components/overlays/HoverCard.jsx"},{"name":"Popconfirm","sourcePath":"components/overlays/Popconfirm.jsx"},{"name":"Popover","sourcePath":"components/overlays/Popover.jsx"},{"name":"Tour","sourcePath":"components/overlays/Tour.jsx"},{"name":"TextField","sourcePath":"components/textfield/TextField.jsx"}],"sourceHashes":{"components/_i18n/i18n.js":"c6c51a65bd79","components/_i18n/strings.js":"73bc5837fc8e","components/_utils/cx.js":"ad6a747ea116","components/ai/AIDisclosureBadge.jsx":"4c8954aa2d79","components/ai/ChatMessage.jsx":"84076f3488f2","components/ai/CitationList.jsx":"4d12ef868ff3","components/ai/ConfidenceMeter.jsx":"24e43b7f7a47","components/ai/HumanReviewGate.jsx":"922b278dca5c","components/ai/PromptInput.jsx":"7634643dafa5","components/ai/PromptSuggestions.jsx":"02d64117ecfa","components/ai/TypingIndicator.jsx":"149e3a0c7ef8","components/brand/LumiAvatar.jsx":"b6444360cf80","components/button/Button.jsx":"6d902a925eed","components/button/ButtonGroup.jsx":"1a1481f6fb93","components/button/FloatingActionButton.jsx":"ce49fe4129a3","components/data/Accordion.jsx":"fed81278cefd","components/data/AspectRatio.jsx":"6c1e8703891e","components/data/Avatar.jsx":"88c8f59334e6","components/data/Card.jsx":"96e1f9ef44a9","components/data/Carousel.jsx":"caf09a766c42","components/data/Chart.jsx":"1953e9fef120","components/data/CodeBlock.jsx":"04e0bd2507e2","components/data/Collapsible.jsx":"3d961fef83f8","components/data/Comment.jsx":"81469a910d80","components/data/DescriptionList.jsx":"ce50d72546bb","components/data/Divider.jsx":"2d08b4a7b18e","components/data/Image.jsx":"da5c7d4c69af","components/data/Item.jsx":"7e1ac68c4e96","components/data/Kbd.jsx":"3b3d867cbdd8","components/data/List.jsx":"5c221b19efe0","components/data/Masonry.jsx":"a65508731d79","components/data/QRCode.jsx":"6acaf577a7fc","components/data/ScrollArea.jsx":"c2e2ec304b1e","components/data/Sortable.jsx":"315b50256128","components/data/Splitter.jsx":"d231064601e9","components/data/Stat.jsx":"35734a801a71","components/data/Terminal.jsx":"3a3cd4477814","components/data/Timeline.jsx":"22058a781a98","components/data/Tooltip.jsx":"4d027aff5559","components/data/Tree.jsx":"9097456de50f","components/data/Watermark.jsx":"4be0b6230d47","components/data/qr-encode.js":"14e8de77df5f","components/datatable/DataGrid.jsx":"e4aa4a47d05e","components/datatable/DataTable.jsx":"fbfad5570592","components/datatable/TreeTable.jsx":"597c35135ebb","components/dialog/Dialog.jsx":"7af51ee28ab6","components/feedback/Alert.jsx":"0bbe4e5f467a","components/feedback/Badge.jsx":"e691ae1634f8","components/feedback/EmptyState.jsx":"9a9c00523cec","components/feedback/ProgressBar.jsx":"1a66335d5d9e","components/feedback/Result.jsx":"c168f8dcb009","components/feedback/Skeleton.jsx":"86ee345cb412","components/feedback/Spinner.jsx":"cad2ed4008a6","components/feedback/StatusIndicator.jsx":"d27a5fc3db2a","components/feedback/Tag.jsx":"13d288da9f38","components/feedback/Toast.jsx":"38cc9a82ede9","components/forms/Calendar.jsx":"8a66cbe766af","components/forms/Cascader.jsx":"ada31ccaf716","components/forms/Checkbox.jsx":"2badb7b3b72b","components/forms/ColorPicker.jsx":"d04db0ee7678","components/forms/Combobox.jsx":"3f2fb4d28922","components/forms/DatePicker.jsx":"7a6c18e1e635","components/forms/Editor.jsx":"d7bed6e52cff","components/forms/FileUpload.jsx":"e8031a109093","components/forms/Form.jsx":"f7952da4b73a","components/forms/InlineEdit.jsx":"5ffb40598769","components/forms/InputGroup.jsx":"06d6c22420d0","components/forms/InputOTP.jsx":"c3a5d02ffebc","components/forms/Mentions.jsx":"04bc4b5a4ace","components/forms/NativeSelect.jsx":"d6f6d300f75d","components/forms/NumberField.jsx":"44975fd25721","components/forms/RadioGroup.jsx":"1a64f7600e01","components/forms/Rating.jsx":"06d345ecc5f4","components/forms/SearchField.jsx":"2a12c65900ec","components/forms/SegmentedControl.jsx":"1f143c62fb56","components/forms/Select.jsx":"1b5ba905b339","components/forms/Slider.jsx":"69aa1a5f1af8","components/forms/Switch.jsx":"0b37cfa41979","components/forms/TagInput.jsx":"2d1554422097","components/forms/Textarea.jsx":"cf244fee0e37","components/forms/TimePicker.jsx":"165192c8760c","components/forms/Toggle.jsx":"338453a34669","components/forms/Transfer.jsx":"dd0c02d1014e","components/forms/TreeSelect.jsx":"a5b1b91d1d6a","components/icon/Icon.jsx":"379dc6de903e","components/logo/Logo.jsx":"445c5f5fbd40","components/logo/logo-data.js":"f1a251a50c8f","components/navigation/Anchor.jsx":"491fb330e718","components/navigation/BackTop.jsx":"5cac0081fc20","components/navigation/Breadcrumb.jsx":"45636f3b0a51","components/navigation/CommandPalette.jsx":"2b5b30906b8b","components/navigation/Dock.jsx":"3073e2870c65","components/navigation/HotKeys.jsx":"f0f707a3335d","components/navigation/Link.jsx":"d4fe3fcffd2c","components/navigation/Menu.jsx":"6786788f13f8","components/navigation/Menubar.jsx":"1aa64f59891e","components/navigation/NavigationMenu.jsx":"020279fd7f79","components/navigation/Pagination.jsx":"a1fc19bfd104","components/navigation/Sidebar.jsx":"d578b013ad33","components/navigation/Steps.jsx":"85cb20bd45da","components/navigation/Tabs.jsx":"5d5921da1644","components/navigation/Toolbar.jsx":"9727d7bdbc5f","components/overlays/AlertDialog.jsx":"2ed7fbde4a5a","components/overlays/ContextMenu.jsx":"e7e3386d89ac","components/overlays/Drawer.jsx":"8227143abbfa","components/overlays/HoverCard.jsx":"5cfabde80960","components/overlays/Popconfirm.jsx":"55f08dba5f38","components/overlays/Popover.jsx":"1582f7fa5533","components/overlays/Tour.jsx":"9901ee649da0","components/textfield/TextField.jsx":"c06bd9729f20","image-slot.js":"0394ad34f685","tokens/tokens.js":"02cfbae229ac","ui_kits/status-hub/StatusHub.jsx":"88dfd93e1664","ui_kits/status-hub/data.js":"664bbbb30cb3","ui_kits/website/Website.jsx":"c3a0938d9279","ui_kits/website/copy.js":"9d42c52c17cb"},"inlinedExternals":[],"unexposedExports":[{"name":"cx","sourcePath":"components/_utils/cx.js"},{"name":"formatCurrency","sourcePath":"components/_i18n/i18n.js"},{"name":"formatDate","sourcePath":"components/_i18n/i18n.js"},{"name":"formatNumber","sourcePath":"components/_i18n/i18n.js"},{"name":"getPath","sourcePath":"components/forms/Form.jsx"},{"name":"makeT","sourcePath":"components/_i18n/i18n.js"},{"name":"monthName","sourcePath":"components/_i18n/i18n.js"},{"name":"qrMatrix","sourcePath":"components/data/qr-encode.js"},{"name":"resolveLang","sourcePath":"components/_i18n/i18n.js"},{"name":"setPath","sourcePath":"components/forms/Form.jsx"},{"name":"strings","sourcePath":"components/_i18n/strings.js"},{"name":"tokens","sourcePath":"tokens/tokens.js"},{"name":"tr","sourcePath":"components/_i18n/i18n.js"},{"name":"useLang","sourcePath":"components/_i18n/i18n.js"}]} */
|
|
1
|
+
/* @ds-bundle: {"format":4,"namespace":"CyberSkillDesignSystem_847b3c","components":[{"name":"AIDisclosureBadge","sourcePath":"components/ai/AIDisclosureBadge.jsx"},{"name":"ChatMessage","sourcePath":"components/ai/ChatMessage.jsx"},{"name":"CitationList","sourcePath":"components/ai/CitationList.jsx"},{"name":"ConfidenceMeter","sourcePath":"components/ai/ConfidenceMeter.jsx"},{"name":"HumanReviewGate","sourcePath":"components/ai/HumanReviewGate.jsx"},{"name":"PromptInput","sourcePath":"components/ai/PromptInput.jsx"},{"name":"PromptSuggestions","sourcePath":"components/ai/PromptSuggestions.jsx"},{"name":"TypingIndicator","sourcePath":"components/ai/TypingIndicator.jsx"},{"name":"LumiAvatar","sourcePath":"components/brand/LumiAvatar.jsx"},{"name":"Button","sourcePath":"components/button/Button.jsx"},{"name":"ButtonGroup","sourcePath":"components/button/ButtonGroup.jsx"},{"name":"FloatingActionButton","sourcePath":"components/button/FloatingActionButton.jsx"},{"name":"Accordion","sourcePath":"components/data/Accordion.jsx"},{"name":"AspectRatio","sourcePath":"components/data/AspectRatio.jsx"},{"name":"Avatar","sourcePath":"components/data/Avatar.jsx"},{"name":"AvatarGroup","sourcePath":"components/data/Avatar.jsx"},{"name":"Card","sourcePath":"components/data/Card.jsx"},{"name":"CardHeader","sourcePath":"components/data/Card.jsx"},{"name":"CardBody","sourcePath":"components/data/Card.jsx"},{"name":"CardFooter","sourcePath":"components/data/Card.jsx"},{"name":"Carousel","sourcePath":"components/data/Carousel.jsx"},{"name":"Chart","sourcePath":"components/data/Chart.jsx"},{"name":"CodeBlock","sourcePath":"components/data/CodeBlock.jsx"},{"name":"Collapsible","sourcePath":"components/data/Collapsible.jsx"},{"name":"Comment","sourcePath":"components/data/Comment.jsx"},{"name":"DescriptionList","sourcePath":"components/data/DescriptionList.jsx"},{"name":"Divider","sourcePath":"components/data/Divider.jsx"},{"name":"Image","sourcePath":"components/data/Image.jsx"},{"name":"Item","sourcePath":"components/data/Item.jsx"},{"name":"Kbd","sourcePath":"components/data/Kbd.jsx"},{"name":"List","sourcePath":"components/data/List.jsx"},{"name":"ListItem","sourcePath":"components/data/List.jsx"},{"name":"Masonry","sourcePath":"components/data/Masonry.jsx"},{"name":"QRCode","sourcePath":"components/data/QRCode.jsx"},{"name":"ScrollArea","sourcePath":"components/data/ScrollArea.jsx"},{"name":"Sortable","sourcePath":"components/data/Sortable.jsx"},{"name":"Splitter","sourcePath":"components/data/Splitter.jsx"},{"name":"Stat","sourcePath":"components/data/Stat.jsx"},{"name":"Terminal","sourcePath":"components/data/Terminal.jsx"},{"name":"Timeline","sourcePath":"components/data/Timeline.jsx"},{"name":"Tooltip","sourcePath":"components/data/Tooltip.jsx"},{"name":"Tree","sourcePath":"components/data/Tree.jsx"},{"name":"Watermark","sourcePath":"components/data/Watermark.jsx"},{"name":"DataGrid","sourcePath":"components/datatable/DataGrid.jsx"},{"name":"DataTable","sourcePath":"components/datatable/DataTable.jsx"},{"name":"TreeTable","sourcePath":"components/datatable/TreeTable.jsx"},{"name":"Dialog","sourcePath":"components/dialog/Dialog.jsx"},{"name":"Alert","sourcePath":"components/feedback/Alert.jsx"},{"name":"Badge","sourcePath":"components/feedback/Badge.jsx"},{"name":"EmptyState","sourcePath":"components/feedback/EmptyState.jsx"},{"name":"ProgressBar","sourcePath":"components/feedback/ProgressBar.jsx"},{"name":"Result","sourcePath":"components/feedback/Result.jsx"},{"name":"Skeleton","sourcePath":"components/feedback/Skeleton.jsx"},{"name":"Spinner","sourcePath":"components/feedback/Spinner.jsx"},{"name":"StatusIndicator","sourcePath":"components/feedback/StatusIndicator.jsx"},{"name":"Tag","sourcePath":"components/feedback/Tag.jsx"},{"name":"ToastStack","sourcePath":"components/feedback/Toast.jsx"},{"name":"Toast","sourcePath":"components/feedback/Toast.jsx"},{"name":"Calendar","sourcePath":"components/forms/Calendar.jsx"},{"name":"Cascader","sourcePath":"components/forms/Cascader.jsx"},{"name":"Checkbox","sourcePath":"components/forms/Checkbox.jsx"},{"name":"ColorPicker","sourcePath":"components/forms/ColorPicker.jsx"},{"name":"Combobox","sourcePath":"components/forms/Combobox.jsx"},{"name":"DatePicker","sourcePath":"components/forms/DatePicker.jsx"},{"name":"Editor","sourcePath":"components/forms/Editor.jsx"},{"name":"FileUpload","sourcePath":"components/forms/FileUpload.jsx"},{"name":"Form","sourcePath":"components/forms/Form.jsx"},{"name":"FormField","sourcePath":"components/forms/Form.jsx"},{"name":"FormFieldArray","sourcePath":"components/forms/Form.jsx"},{"name":"FormWizard","sourcePath":"components/forms/Form.jsx"},{"name":"InlineEdit","sourcePath":"components/forms/InlineEdit.jsx"},{"name":"InputGroup","sourcePath":"components/forms/InputGroup.jsx"},{"name":"InputOTP","sourcePath":"components/forms/InputOTP.jsx"},{"name":"Mentions","sourcePath":"components/forms/Mentions.jsx"},{"name":"NativeSelect","sourcePath":"components/forms/NativeSelect.jsx"},{"name":"NumberField","sourcePath":"components/forms/NumberField.jsx"},{"name":"Radio","sourcePath":"components/forms/RadioGroup.jsx"},{"name":"RadioGroup","sourcePath":"components/forms/RadioGroup.jsx"},{"name":"Rating","sourcePath":"components/forms/Rating.jsx"},{"name":"SearchField","sourcePath":"components/forms/SearchField.jsx"},{"name":"SegmentedControl","sourcePath":"components/forms/SegmentedControl.jsx"},{"name":"Select","sourcePath":"components/forms/Select.jsx"},{"name":"Slider","sourcePath":"components/forms/Slider.jsx"},{"name":"Switch","sourcePath":"components/forms/Switch.jsx"},{"name":"TagInput","sourcePath":"components/forms/TagInput.jsx"},{"name":"Textarea","sourcePath":"components/forms/Textarea.jsx"},{"name":"TimePicker","sourcePath":"components/forms/TimePicker.jsx"},{"name":"Toggle","sourcePath":"components/forms/Toggle.jsx"},{"name":"Transfer","sourcePath":"components/forms/Transfer.jsx"},{"name":"TreeSelect","sourcePath":"components/forms/TreeSelect.jsx"},{"name":"CS_ICONS","sourcePath":"components/icon/Icon.jsx"},{"name":"Icon","sourcePath":"components/icon/Icon.jsx"},{"name":"Logo","sourcePath":"components/logo/Logo.jsx"},{"name":"CS_LOGO_VIEWBOX","sourcePath":"components/logo/logo-data.js"},{"name":"CS_LOGO_MARK_INNER","sourcePath":"components/logo/logo-data.js"},{"name":"Anchor","sourcePath":"components/navigation/Anchor.jsx"},{"name":"BackTop","sourcePath":"components/navigation/BackTop.jsx"},{"name":"Breadcrumb","sourcePath":"components/navigation/Breadcrumb.jsx"},{"name":"CommandPalette","sourcePath":"components/navigation/CommandPalette.jsx"},{"name":"Dock","sourcePath":"components/navigation/Dock.jsx"},{"name":"HotKeys","sourcePath":"components/navigation/HotKeys.jsx"},{"name":"Link","sourcePath":"components/navigation/Link.jsx"},{"name":"Menu","sourcePath":"components/navigation/Menu.jsx"},{"name":"MenuItem","sourcePath":"components/navigation/Menu.jsx"},{"name":"Menubar","sourcePath":"components/navigation/Menubar.jsx"},{"name":"NavigationMenu","sourcePath":"components/navigation/NavigationMenu.jsx"},{"name":"Pagination","sourcePath":"components/navigation/Pagination.jsx"},{"name":"Sidebar","sourcePath":"components/navigation/Sidebar.jsx"},{"name":"NavItem","sourcePath":"components/navigation/Sidebar.jsx"},{"name":"Steps","sourcePath":"components/navigation/Steps.jsx"},{"name":"Tabs","sourcePath":"components/navigation/Tabs.jsx"},{"name":"Tab","sourcePath":"components/navigation/Tabs.jsx"},{"name":"Toolbar","sourcePath":"components/navigation/Toolbar.jsx"},{"name":"AlertDialog","sourcePath":"components/overlays/AlertDialog.jsx"},{"name":"ContextMenu","sourcePath":"components/overlays/ContextMenu.jsx"},{"name":"Drawer","sourcePath":"components/overlays/Drawer.jsx"},{"name":"HoverCard","sourcePath":"components/overlays/HoverCard.jsx"},{"name":"OverlayProvider","sourcePath":"components/overlays/OverlayManager.jsx"},{"name":"Popconfirm","sourcePath":"components/overlays/Popconfirm.jsx"},{"name":"Popover","sourcePath":"components/overlays/Popover.jsx"},{"name":"Tour","sourcePath":"components/overlays/Tour.jsx"},{"name":"TextField","sourcePath":"components/textfield/TextField.jsx"}],"sourceHashes":{"components/_i18n/i18n.js":"c8fc9f74a4c0","components/_i18n/strings.js":"0e63dc7515d8","components/_utils/cx.js":"ad6a747ea116","components/ai/AIDisclosureBadge.jsx":"4c8954aa2d79","components/ai/ChatMessage.jsx":"344e9c13fcfe","components/ai/CitationList.jsx":"4d12ef868ff3","components/ai/ConfidenceMeter.jsx":"68cf2bba3a9e","components/ai/HumanReviewGate.jsx":"922b278dca5c","components/ai/PromptInput.jsx":"7634643dafa5","components/ai/PromptSuggestions.jsx":"02d64117ecfa","components/ai/TypingIndicator.jsx":"149e3a0c7ef8","components/brand/LumiAvatar.jsx":"b6444360cf80","components/button/Button.jsx":"6d902a925eed","components/button/ButtonGroup.jsx":"1a1481f6fb93","components/button/FloatingActionButton.jsx":"ce49fe4129a3","components/data/Accordion.jsx":"fed81278cefd","components/data/AspectRatio.jsx":"6c1e8703891e","components/data/Avatar.jsx":"88c8f59334e6","components/data/Card.jsx":"96e1f9ef44a9","components/data/Carousel.jsx":"caf09a766c42","components/data/Chart.jsx":"1953e9fef120","components/data/CodeBlock.jsx":"04e0bd2507e2","components/data/Collapsible.jsx":"3d961fef83f8","components/data/Comment.jsx":"81469a910d80","components/data/DescriptionList.jsx":"ce50d72546bb","components/data/Divider.jsx":"2d08b4a7b18e","components/data/Image.jsx":"0ea06cfe1532","components/data/Item.jsx":"7e1ac68c4e96","components/data/Kbd.jsx":"3b3d867cbdd8","components/data/List.jsx":"5c221b19efe0","components/data/Masonry.jsx":"a65508731d79","components/data/QRCode.jsx":"6acaf577a7fc","components/data/ScrollArea.jsx":"c2e2ec304b1e","components/data/Sortable.jsx":"ed7615a9469a","components/data/Splitter.jsx":"d231064601e9","components/data/Stat.jsx":"35734a801a71","components/data/Terminal.jsx":"9d419686bea8","components/data/Timeline.jsx":"22058a781a98","components/data/Tooltip.jsx":"3f30c6b1bf1d","components/data/Tree.jsx":"a1d33dec0451","components/data/Watermark.jsx":"66b7b71488d8","components/data/qr-encode.js":"14e8de77df5f","components/datatable/DataGrid.jsx":"c84fe5d5b8cc","components/datatable/DataTable.jsx":"2a7c3bda5142","components/datatable/TreeTable.jsx":"597c35135ebb","components/dialog/Dialog.jsx":"8ef7722c37c4","components/feedback/Alert.jsx":"4dbf685af3e1","components/feedback/Badge.jsx":"e691ae1634f8","components/feedback/EmptyState.jsx":"5ba1437e6d81","components/feedback/ProgressBar.jsx":"c63a431e9eed","components/feedback/Result.jsx":"c168f8dcb009","components/feedback/Skeleton.jsx":"86ee345cb412","components/feedback/Spinner.jsx":"cad2ed4008a6","components/feedback/StatusIndicator.jsx":"d27a5fc3db2a","components/feedback/Tag.jsx":"13d288da9f38","components/feedback/Toast.jsx":"6a7832751669","components/forms/Calendar.jsx":"8a66cbe766af","components/forms/Cascader.jsx":"63f35db7101d","components/forms/Checkbox.jsx":"2badb7b3b72b","components/forms/ColorPicker.jsx":"d04db0ee7678","components/forms/Combobox.jsx":"3f2fb4d28922","components/forms/DatePicker.jsx":"7a6c18e1e635","components/forms/Editor.jsx":"194484fb9dc4","components/forms/FileUpload.jsx":"509dfaff8bea","components/forms/Form.jsx":"6945025a205f","components/forms/InlineEdit.jsx":"5ffb40598769","components/forms/InputGroup.jsx":"06d6c22420d0","components/forms/InputOTP.jsx":"c3a5d02ffebc","components/forms/Mentions.jsx":"3924619137f3","components/forms/NativeSelect.jsx":"d6f6d300f75d","components/forms/NumberField.jsx":"44975fd25721","components/forms/RadioGroup.jsx":"1a64f7600e01","components/forms/Rating.jsx":"bd5c94741d63","components/forms/SearchField.jsx":"2a12c65900ec","components/forms/SegmentedControl.jsx":"1f143c62fb56","components/forms/Select.jsx":"009495d18845","components/forms/Slider.jsx":"213b7b9edec9","components/forms/Switch.jsx":"3cdf185f757c","components/forms/TagInput.jsx":"2d1554422097","components/forms/Textarea.jsx":"cf244fee0e37","components/forms/TimePicker.jsx":"abd6e54b2983","components/forms/Toggle.jsx":"a5378fd66713","components/forms/Transfer.jsx":"dd0c02d1014e","components/forms/TreeSelect.jsx":"a5b1b91d1d6a","components/icon/Icon.jsx":"280fba8477db","components/logo/Logo.jsx":"445c5f5fbd40","components/logo/logo-data.js":"f1a251a50c8f","components/navigation/Anchor.jsx":"491fb330e718","components/navigation/BackTop.jsx":"5cac0081fc20","components/navigation/Breadcrumb.jsx":"45636f3b0a51","components/navigation/CommandPalette.jsx":"51c25b8416eb","components/navigation/Dock.jsx":"3073e2870c65","components/navigation/HotKeys.jsx":"f0f707a3335d","components/navigation/Link.jsx":"d4fe3fcffd2c","components/navigation/Menu.jsx":"999e1659f8b3","components/navigation/Menubar.jsx":"1aa64f59891e","components/navigation/NavigationMenu.jsx":"020279fd7f79","components/navigation/Pagination.jsx":"a1fc19bfd104","components/navigation/Sidebar.jsx":"d578b013ad33","components/navigation/Steps.jsx":"e26c4a18cdfc","components/navigation/Tabs.jsx":"16964d61f349","components/navigation/Toolbar.jsx":"0f1f25d9e0c5","components/overlays/AlertDialog.jsx":"dcfbb7f61506","components/overlays/ContextMenu.jsx":"e3fb63e7b74f","components/overlays/Drawer.jsx":"a1c719da3f62","components/overlays/HoverCard.jsx":"5cfabde80960","components/overlays/OverlayManager.jsx":"6b58c4dad9da","components/overlays/Popconfirm.jsx":"55f08dba5f38","components/overlays/Popover.jsx":"1582f7fa5533","components/overlays/Tour.jsx":"707cd7464f0c","components/textfield/TextField.jsx":"c06bd9729f20","tokens/tokens.js":"aea97a95111a"},"inlinedExternals":[],"unexposedExports":[{"name":"attachFocusTrap","sourcePath":"components/overlays/OverlayManager.jsx"},{"name":"cx","sourcePath":"components/_utils/cx.js"},{"name":"focusableSelector","sourcePath":"components/overlays/OverlayManager.jsx"},{"name":"formatCurrency","sourcePath":"components/_i18n/i18n.js"},{"name":"formatDate","sourcePath":"components/_i18n/i18n.js"},{"name":"formatNumber","sourcePath":"components/_i18n/i18n.js"},{"name":"getOverlayManager","sourcePath":"components/overlays/OverlayManager.jsx"},{"name":"getPath","sourcePath":"components/forms/Form.jsx"},{"name":"makeT","sourcePath":"components/_i18n/i18n.js"},{"name":"monthName","sourcePath":"components/_i18n/i18n.js"},{"name":"qrMatrix","sourcePath":"components/data/qr-encode.js"},{"name":"reorderItems","sourcePath":"components/data/Sortable.jsx"},{"name":"resolveLang","sourcePath":"components/_i18n/i18n.js"},{"name":"setPath","sourcePath":"components/forms/Form.jsx"},{"name":"strings","sourcePath":"components/_i18n/strings.js"},{"name":"tokens","sourcePath":"tokens/tokens.js"},{"name":"tr","sourcePath":"components/_i18n/i18n.js"},{"name":"useLang","sourcePath":"components/_i18n/i18n.js"},{"name":"useOverlayLayer","sourcePath":"components/overlays/OverlayManager.jsx"}]} */
|
|
2
2
|
|
|
3
3
|
(() => {
|
|
4
4
|
|
|
@@ -20,8 +20,8 @@ const strings = {
|
|
|
20
20
|
vi: { label: "Đường dẫn" }
|
|
21
21
|
},
|
|
22
22
|
CommandPalette: {
|
|
23
|
-
en: { placeholder: "Type a command or search…", empty: "No results
|
|
24
|
-
vi: { placeholder: "Nhập lệnh hoặc tìm kiếm…", empty: "Không
|
|
23
|
+
en: { placeholder: "Type a command or search…", empty: "No results", aria: "Command palette", esc: "Esc" },
|
|
24
|
+
vi: { placeholder: "Nhập lệnh hoặc tìm kiếm…", empty: "Không có kết quả", aria: "Bảng lệnh", esc: "Esc" }
|
|
25
25
|
},
|
|
26
26
|
SearchField: {
|
|
27
27
|
en: { placeholder: "Search…", clear: "Clear search" },
|
|
@@ -44,12 +44,22 @@ const strings = {
|
|
|
44
44
|
vi: { confirm: "Xác nhận", cancel: "Hủy" }
|
|
45
45
|
},
|
|
46
46
|
Drawer: {
|
|
47
|
-
en: { close: "Close", panel: "
|
|
48
|
-
vi: { close: "Đóng", panel: "Bảng" }
|
|
47
|
+
en: { close: "Close", panel: "Side panel" },
|
|
48
|
+
vi: { close: "Đóng", panel: "Bảng bên" }
|
|
49
49
|
},
|
|
50
50
|
DataTable: {
|
|
51
|
-
en: {
|
|
52
|
-
|
|
51
|
+
en: {
|
|
52
|
+
empty: "No results",
|
|
53
|
+
loading: "Loading table",
|
|
54
|
+
error: "We couldn't load this table",
|
|
55
|
+
errorHint: "Check your connection, then try again."
|
|
56
|
+
},
|
|
57
|
+
vi: {
|
|
58
|
+
empty: "Không có kết quả",
|
|
59
|
+
loading: "Đang tải bảng",
|
|
60
|
+
error: "Không tải được bảng này",
|
|
61
|
+
errorHint: "Kiểm tra kết nối, rồi thử lại."
|
|
62
|
+
}
|
|
53
63
|
},
|
|
54
64
|
ConfidenceMeter: {
|
|
55
65
|
en: { label: "Confidence", low: "Low", medium: "Medium", high: "High" },
|
|
@@ -61,10 +71,10 @@ const strings = {
|
|
|
61
71
|
},
|
|
62
72
|
Tag: {
|
|
63
73
|
en: { remove: "Remove" },
|
|
64
|
-
vi: { remove: "Gỡ
|
|
74
|
+
vi: { remove: "Gỡ" }
|
|
65
75
|
},
|
|
66
76
|
Toast: {
|
|
67
|
-
en: { notifications: "Notifications", dismiss: "
|
|
77
|
+
en: { notifications: "Notifications", dismiss: "Close" },
|
|
68
78
|
vi: { notifications: "Thông báo", dismiss: "Đóng" }
|
|
69
79
|
},
|
|
70
80
|
TypingIndicator: {
|
|
@@ -76,12 +86,12 @@ const strings = {
|
|
|
76
86
|
vi: { toolbar: "Định dạng", bold: "Đậm", italic: "Nghiêng", list: "Danh sách", area: "Văn bản" }
|
|
77
87
|
},
|
|
78
88
|
Terminal: {
|
|
79
|
-
en: { input: "Terminal input" },
|
|
80
|
-
vi: { input: "Ô nhập lệnh" }
|
|
89
|
+
en: { input: "Terminal input", title: "cyberskill — zsh" },
|
|
90
|
+
vi: { input: "Ô nhập lệnh", title: "cyberskill — zsh" }
|
|
81
91
|
},
|
|
82
92
|
DataGrid: {
|
|
83
|
-
en: { empty: "No
|
|
84
|
-
vi: { empty: "Không có
|
|
93
|
+
en: { empty: "No results", selectAll: "Select all rows", selectRow: "Select row", pin: "Pin this column", pinNamed: "Pin column {name}" },
|
|
94
|
+
vi: { empty: "Không có kết quả", selectAll: "Chọn tất cả", selectRow: "Chọn dòng", pin: "Ghim cột này", pinNamed: "Ghim cột {name}" }
|
|
85
95
|
},
|
|
86
96
|
Splitter: {
|
|
87
97
|
en: { label: "Resize panes" },
|
|
@@ -115,9 +125,13 @@ const strings = {
|
|
|
115
125
|
en: { placeholder: "Add a note — @ to mention" },
|
|
116
126
|
vi: { placeholder: "Thêm ghi chú — gõ @ để nhắc tên" }
|
|
117
127
|
},
|
|
128
|
+
Sortable: {
|
|
129
|
+
en: { moveUp: "Move up", moveDown: "Move down" },
|
|
130
|
+
vi: { moveUp: "Di chuyển lên", moveDown: "Di chuyển xuống" }
|
|
131
|
+
},
|
|
118
132
|
Form: {
|
|
119
|
-
en: { summary: "Please fix the following:", required: "required", requiredField: "
|
|
120
|
-
vi: { summary: "Vui lòng sửa các mục sau:", required: "bắt buộc", requiredField: "
|
|
133
|
+
en: { summary: "Please fix the following:", required: "required", requiredField: "Enter a value" },
|
|
134
|
+
vi: { summary: "Vui lòng sửa các mục sau:", required: "bắt buộc", requiredField: "Vui lòng nhập giá trị" }
|
|
121
135
|
},
|
|
122
136
|
BackTop: {
|
|
123
137
|
en: { label: "Back to top" },
|
|
@@ -125,7 +139,7 @@ const strings = {
|
|
|
125
139
|
},
|
|
126
140
|
Carousel: {
|
|
127
141
|
en: { prev: "Previous slide", next: "Next slide", slide: "Slide" },
|
|
128
|
-
vi: { prev: "
|
|
142
|
+
vi: { prev: "Ảnh trước", next: "Ảnh sau", slide: "Ảnh" }
|
|
129
143
|
},
|
|
130
144
|
Comment: {
|
|
131
145
|
en: { reply: "Reply" },
|
|
@@ -140,12 +154,12 @@ const strings = {
|
|
|
140
154
|
vi: { more: "Thao tác khác" }
|
|
141
155
|
},
|
|
142
156
|
Popconfirm: {
|
|
143
|
-
en: { ok: "
|
|
144
|
-
vi: { ok: "
|
|
157
|
+
en: { ok: "Confirm", cancel: "Cancel" },
|
|
158
|
+
vi: { ok: "Xác nhận", cancel: "Hủy" }
|
|
145
159
|
},
|
|
146
160
|
Result: {
|
|
147
|
-
en: { success: "Done", error: "
|
|
148
|
-
vi: { success: "Hoàn tất", error: "
|
|
161
|
+
en: { success: "Done", error: "We couldn't complete that", warning: "Please check", info: "Here's what happened" },
|
|
162
|
+
vi: { success: "Hoàn tất", error: "Chúng tôi chưa hoàn tất được", warning: "Vui lòng kiểm tra", info: "Đây là điều đã xảy ra" }
|
|
149
163
|
},
|
|
150
164
|
InlineEdit: {
|
|
151
165
|
en: { edit: "Edit", empty: "Add text" },
|
|
@@ -164,8 +178,8 @@ const strings = {
|
|
|
164
178
|
vi: { label: "Giờ" }
|
|
165
179
|
},
|
|
166
180
|
Combobox: {
|
|
167
|
-
en: { placeholder: "Select or type…", empty: "No
|
|
168
|
-
vi: { placeholder: "Chọn hoặc nhập…", empty: "Không có kết quả
|
|
181
|
+
en: { placeholder: "Select or type…", empty: "No results" },
|
|
182
|
+
vi: { placeholder: "Chọn hoặc nhập…", empty: "Không có kết quả" }
|
|
169
183
|
},
|
|
170
184
|
InputGroup: {
|
|
171
185
|
en: { clear: "Clear", show: "Show password", hide: "Hide password", input: "Input" },
|
|
@@ -190,6 +204,58 @@ const strings = {
|
|
|
190
204
|
HumanReviewGate: {
|
|
191
205
|
en: { aria: "Human review gate", risk: "review required", approve: "Approve", reject: "Reject", reviewer: "Reviewer" },
|
|
192
206
|
vi: { aria: "Cổng kiểm duyệt của con người", risk: "cần xem xét", approve: "Phê duyệt", reject: "Từ chối", reviewer: "Người duyệt" }
|
|
207
|
+
},
|
|
208
|
+
Switch: {
|
|
209
|
+
en: { on: "On", off: "Off" },
|
|
210
|
+
vi: { on: "Bật", off: "Tắt" }
|
|
211
|
+
},
|
|
212
|
+
Tabs: {
|
|
213
|
+
en: { list: "Tabs" },
|
|
214
|
+
vi: { list: "Tab" }
|
|
215
|
+
},
|
|
216
|
+
EmptyState: {
|
|
217
|
+
en: { title: "Nothing here yet", action: "Create your first item" },
|
|
218
|
+
vi: { title: "Chưa có gì ở đây", action: "Tạo mục đầu tiên" }
|
|
219
|
+
},
|
|
220
|
+
Alert: {
|
|
221
|
+
en: { dismiss: "Close" },
|
|
222
|
+
vi: { dismiss: "Đóng" }
|
|
223
|
+
},
|
|
224
|
+
Select: {
|
|
225
|
+
en: { placeholder: "Select…" },
|
|
226
|
+
vi: { placeholder: "Chọn…" }
|
|
227
|
+
},
|
|
228
|
+
Slider: {
|
|
229
|
+
en: { label: "Slider" },
|
|
230
|
+
vi: { label: "Thanh trượt" }
|
|
231
|
+
},
|
|
232
|
+
Steps: {
|
|
233
|
+
en: { label: "Steps" },
|
|
234
|
+
vi: { label: "Các bước" }
|
|
235
|
+
},
|
|
236
|
+
Toggle: {
|
|
237
|
+
en: { pressed: "Pressed", unpressed: "Not pressed" },
|
|
238
|
+
vi: { pressed: "Đang bật", unpressed: "Chưa bật" }
|
|
239
|
+
},
|
|
240
|
+
Menu: {
|
|
241
|
+
en: { menu: "Menu" },
|
|
242
|
+
vi: { menu: "Trình đơn" }
|
|
243
|
+
},
|
|
244
|
+
ContextMenu: {
|
|
245
|
+
en: { menu: "Context menu" },
|
|
246
|
+
vi: { menu: "Trình đơn ngữ cảnh" }
|
|
247
|
+
},
|
|
248
|
+
Tooltip: {
|
|
249
|
+
en: { label: "Tooltip" },
|
|
250
|
+
vi: { label: "Chú thích" }
|
|
251
|
+
},
|
|
252
|
+
ProgressBar: {
|
|
253
|
+
en: { label: "Progress" },
|
|
254
|
+
vi: { label: "Tiến độ" }
|
|
255
|
+
},
|
|
256
|
+
ChatMessage: {
|
|
257
|
+
en: { you: "You" },
|
|
258
|
+
vi: { you: "Bạn" }
|
|
193
259
|
}
|
|
194
260
|
};
|
|
195
261
|
Object.assign(__ds_scope, { strings });
|
|
@@ -222,7 +288,16 @@ function useLang(propLang) {
|
|
|
222
288
|
const ref = React.useRef(null);
|
|
223
289
|
const [lang, setLang] = React.useState(() => resolveLang(propLang, null));
|
|
224
290
|
React.useLayoutEffect(() => {
|
|
225
|
-
|
|
291
|
+
const el = ref.current;
|
|
292
|
+
setLang(resolveLang(propLang, el));
|
|
293
|
+
if (!el || propLang) return void 0;
|
|
294
|
+
const obs = new MutationObserver(() => setLang(resolveLang(propLang, el)));
|
|
295
|
+
let node = el.parentElement;
|
|
296
|
+
while (node) {
|
|
297
|
+
obs.observe(node, { attributes: true, attributeFilter: ["lang"] });
|
|
298
|
+
node = node.parentElement;
|
|
299
|
+
}
|
|
300
|
+
return () => obs.disconnect();
|
|
226
301
|
}, [propLang]);
|
|
227
302
|
return [ref, lang];
|
|
228
303
|
}
|
|
@@ -243,8 +318,21 @@ function formatNumber(n, lang) {
|
|
|
243
318
|
if (n == null || isNaN(n)) return "";
|
|
244
319
|
return new Intl.NumberFormat(lang === "vi" ? "vi-VN" : "en-US").format(n);
|
|
245
320
|
}
|
|
246
|
-
function formatCurrency(n,
|
|
321
|
+
function formatCurrency(n, langOrOpts) {
|
|
247
322
|
if (n == null || isNaN(n)) return "";
|
|
323
|
+
const opts = langOrOpts && typeof langOrOpts === "object" ? langOrOpts : null;
|
|
324
|
+
if (opts && opts.currency) {
|
|
325
|
+
let locale = opts.locale;
|
|
326
|
+
if (!locale) {
|
|
327
|
+
locale = opts.lang === "vi" ? "vi-VN" : "en-US";
|
|
328
|
+
}
|
|
329
|
+
return new Intl.NumberFormat(locale, {
|
|
330
|
+
style: "currency",
|
|
331
|
+
currency: String(opts.currency).toUpperCase(),
|
|
332
|
+
currencyDisplay: "symbol"
|
|
333
|
+
}).format(Number(n));
|
|
334
|
+
}
|
|
335
|
+
const lang = typeof langOrOpts === "string" ? langOrOpts : opts && opts.lang || "en";
|
|
248
336
|
return lang === "vi" ? new Intl.NumberFormat("vi-VN").format(n) + " ₫" : "$" + new Intl.NumberFormat("en-US").format(n);
|
|
249
337
|
}
|
|
250
338
|
Object.assign(__ds_scope, { resolveLang, tr, makeT, useLang, formatDate, monthName, formatNumber, formatCurrency });
|
|
@@ -287,11 +375,15 @@ Object.assign(__ds_scope, { AIDisclosureBadge });
|
|
|
287
375
|
|
|
288
376
|
// components/ai/ChatMessage.jsx
|
|
289
377
|
try { (() => {
|
|
378
|
+
const { makeT, useLang } = __ds_scope;
|
|
290
379
|
const { cx } = __ds_scope;
|
|
291
|
-
function ChatMessage({ role = "lumi", name, avatar,
|
|
380
|
+
function ChatMessage({ role = "lumi", name, avatar, lang, className, children }) {
|
|
292
381
|
const isUser = role === "user";
|
|
293
|
-
const
|
|
294
|
-
|
|
382
|
+
const [ref, L] = useLang(lang);
|
|
383
|
+
const t = makeT("ChatMessage", L);
|
|
384
|
+
const you = t("you");
|
|
385
|
+
const defaultAvatar = isUser ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, you) : /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: "M12 3l1.8 5.4L19 10l-5.2 1.6L12 17l-1.8-5.4L5 10l5.2-1.6z" }));
|
|
386
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-chat-msg", isUser ? "cs-chat-msg--user" : "cs-chat-msg--lumi", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__avatar" }, avatar ?? defaultAvatar), /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__col" }, /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__name" }, name ?? (isUser ? you : "Lumi")), /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__bubble" }, children)));
|
|
295
387
|
}
|
|
296
388
|
Object.assign(__ds_scope, { ChatMessage });
|
|
297
389
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/ai/ChatMessage.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -319,19 +411,21 @@ const TONE = {
|
|
|
319
411
|
};
|
|
320
412
|
function ConfidenceMeter({ value, level, segments = 5, label, lang, className }) {
|
|
321
413
|
let tone, filled;
|
|
414
|
+
const segs = Math.max(1, Number(segments) || 5);
|
|
322
415
|
if (value != null) {
|
|
323
|
-
|
|
324
|
-
|
|
416
|
+
const v = Math.max(0, Math.min(1, Number(value) || 0));
|
|
417
|
+
tone = v < 0.4 ? "low" : v < 0.75 ? "medium" : "high";
|
|
418
|
+
filled = Math.max(0, Math.min(segs, Math.round(v * segs)));
|
|
325
419
|
} else {
|
|
326
|
-
tone = level
|
|
327
|
-
filled = Math.round(TONE[tone].fill / 5 *
|
|
420
|
+
tone = TONE[level] ? level : "medium";
|
|
421
|
+
filled = Math.max(0, Math.min(segs, Math.round(TONE[tone].fill / 5 * segs)));
|
|
328
422
|
}
|
|
329
423
|
const meta = TONE[tone];
|
|
330
424
|
const [ref, L] = useLang(lang);
|
|
331
425
|
const t = makeT("ConfidenceMeter", L);
|
|
332
426
|
const lbl = label != null ? label : t("label");
|
|
333
427
|
const levelText = t(tone);
|
|
334
|
-
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-confidence", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-confidence__head" }, /* @__PURE__ */ React.createElement("span", null, lbl), /* @__PURE__ */ React.createElement("span", { className: "cs-confidence__level", style: { color: meta.color } }, levelText)), /* @__PURE__ */ React.createElement("div", { className: "cs-confidence__track", role: "meter", "aria-valuemin": 0, "aria-valuemax":
|
|
428
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-confidence", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-confidence__head" }, /* @__PURE__ */ React.createElement("span", null, lbl), /* @__PURE__ */ React.createElement("span", { className: "cs-confidence__level", style: { color: meta.color } }, levelText)), /* @__PURE__ */ React.createElement("div", { className: "cs-confidence__track", role: "meter", "aria-valuemin": 0, "aria-valuemax": segs, "aria-valuenow": filled, "aria-label": lbl + ": " + levelText }, Array.from({ length: segs }).map((_, i) => /* @__PURE__ */ React.createElement("span", { key: i, className: "cs-confidence__seg", style: i < filled ? { background: meta.color } : void 0 }))));
|
|
335
429
|
}
|
|
336
430
|
Object.assign(__ds_scope, { ConfidenceMeter });
|
|
337
431
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/ai/ConfidenceMeter.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -822,10 +916,23 @@ Object.assign(__ds_scope, { ScrollArea });
|
|
|
822
916
|
|
|
823
917
|
// components/data/Sortable.jsx
|
|
824
918
|
try { (() => {
|
|
919
|
+
const { makeT, useLang } = __ds_scope;
|
|
825
920
|
const { cx } = __ds_scope;
|
|
826
|
-
function
|
|
921
|
+
function reorderItems(items, from, to) {
|
|
922
|
+
if (from < 0 || to < 0 || from >= items.length || to >= items.length || from === to) return items;
|
|
923
|
+
const next = [...items];
|
|
924
|
+
next.splice(to, 0, next.splice(from, 1)[0]);
|
|
925
|
+
return next;
|
|
926
|
+
}
|
|
927
|
+
function Sortable({ items = [], onChange, lang, className }) {
|
|
827
928
|
const [dragKey, setDragKey] = React.useState(null);
|
|
828
929
|
const [over, setOver] = React.useState(null);
|
|
930
|
+
const [ref, L] = useLang(lang);
|
|
931
|
+
const t = makeT("Sortable", L);
|
|
932
|
+
const move = (from, to) => {
|
|
933
|
+
const next = reorderItems(items, from, to);
|
|
934
|
+
if (next !== items) onChange && onChange(next);
|
|
935
|
+
};
|
|
829
936
|
const drop = () => {
|
|
830
937
|
if (dragKey == null || over == null || dragKey === over) {
|
|
831
938
|
setDragKey(null);
|
|
@@ -834,19 +941,23 @@ function Sortable({ items = [], onChange, className }) {
|
|
|
834
941
|
}
|
|
835
942
|
const from = items.findIndex((i) => i.key === dragKey);
|
|
836
943
|
const to = items.findIndex((i) => i.key === over);
|
|
837
|
-
|
|
838
|
-
next.splice(to, 0, next.splice(from, 1)[0]);
|
|
839
|
-
onChange && onChange(next);
|
|
944
|
+
move(from, to);
|
|
840
945
|
setDragKey(null);
|
|
841
946
|
setOver(null);
|
|
842
947
|
};
|
|
843
|
-
return /* @__PURE__ */ React.createElement("ul", { className: cx("cs-sortable", className) }, items.map((it) => /* @__PURE__ */ React.createElement(
|
|
948
|
+
return /* @__PURE__ */ React.createElement("ul", { ref, className: cx("cs-sortable", className) }, items.map((it, idx) => /* @__PURE__ */ React.createElement(
|
|
844
949
|
"li",
|
|
845
950
|
{
|
|
846
951
|
key: it.key,
|
|
847
952
|
draggable: true,
|
|
848
953
|
className: cx("cs-sortable__item", dragKey === it.key && "is-dragging", over === it.key && "is-over"),
|
|
849
|
-
onDragStart: () =>
|
|
954
|
+
onDragStart: (e) => {
|
|
955
|
+
if (e.target.closest && e.target.closest(".cs-sortable__ops")) {
|
|
956
|
+
e.preventDefault();
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
setDragKey(it.key);
|
|
960
|
+
},
|
|
850
961
|
onDragOver: (e) => {
|
|
851
962
|
e.preventDefault();
|
|
852
963
|
setOver(it.key);
|
|
@@ -855,10 +966,31 @@ function Sortable({ items = [], onChange, className }) {
|
|
|
855
966
|
onDragEnd: drop
|
|
856
967
|
},
|
|
857
968
|
/* @__PURE__ */ React.createElement("span", { className: "cs-sortable__grip", "aria-hidden": "true" }, "⠿"),
|
|
858
|
-
it.label
|
|
969
|
+
/* @__PURE__ */ React.createElement("span", { className: "cs-sortable__label" }, it.label),
|
|
970
|
+
/* @__PURE__ */ React.createElement("span", { className: "cs-sortable__ops", onMouseDown: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement(
|
|
971
|
+
"button",
|
|
972
|
+
{
|
|
973
|
+
type: "button",
|
|
974
|
+
className: "cs-button cs-button--secondary cs-button--xs",
|
|
975
|
+
"aria-label": t("moveUp"),
|
|
976
|
+
disabled: idx === 0,
|
|
977
|
+
onClick: () => move(idx, idx - 1)
|
|
978
|
+
},
|
|
979
|
+
"↑"
|
|
980
|
+
), /* @__PURE__ */ React.createElement(
|
|
981
|
+
"button",
|
|
982
|
+
{
|
|
983
|
+
type: "button",
|
|
984
|
+
className: "cs-button cs-button--secondary cs-button--xs",
|
|
985
|
+
"aria-label": t("moveDown"),
|
|
986
|
+
disabled: idx === items.length - 1,
|
|
987
|
+
onClick: () => move(idx, idx + 1)
|
|
988
|
+
},
|
|
989
|
+
"↓"
|
|
990
|
+
))
|
|
859
991
|
)));
|
|
860
992
|
}
|
|
861
|
-
Object.assign(__ds_scope, { Sortable });
|
|
993
|
+
Object.assign(__ds_scope, { reorderItems, Sortable });
|
|
862
994
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Sortable.jsx", error: String((e && e.message) || e) }); }
|
|
863
995
|
|
|
864
996
|
// components/data/Splitter.jsx
|
|
@@ -922,18 +1054,19 @@ Object.assign(__ds_scope, { Stat });
|
|
|
922
1054
|
try { (() => {
|
|
923
1055
|
const { makeT, useLang } = __ds_scope;
|
|
924
1056
|
const { cx } = __ds_scope;
|
|
925
|
-
function Terminal({ title
|
|
1057
|
+
function Terminal({ title, welcome, onCommand, prompt = "➜", lang, className }) {
|
|
926
1058
|
const [hist, setHist] = React.useState(() => welcome ? [{ out: welcome }] : []);
|
|
927
1059
|
const [q, setQ] = React.useState("");
|
|
928
1060
|
const [ref, L] = useLang(lang);
|
|
929
1061
|
const t = makeT("Terminal", L);
|
|
1062
|
+
const barTitle = title ?? t("title");
|
|
930
1063
|
const run = () => {
|
|
931
1064
|
if (!q.trim()) return;
|
|
932
1065
|
const res = onCommand ? onCommand(q.trim()) : "";
|
|
933
1066
|
setHist((h) => [...h, { cmd: q }, ...res ? [{ out: res }] : []]);
|
|
934
1067
|
setQ("");
|
|
935
1068
|
};
|
|
936
|
-
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-terminal", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__bar" }, /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("span", null,
|
|
1069
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-terminal", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__bar" }, /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("span", null, barTitle)), /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__body" }, hist.map((l, i) => l.cmd != null ? /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-terminal__line" }, /* @__PURE__ */ React.createElement("span", { className: "p" }, prompt), " ", l.cmd) : /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-terminal__out" }, l.out)), /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__line" }, /* @__PURE__ */ React.createElement("span", { className: "p" }, prompt), /* @__PURE__ */ React.createElement(
|
|
937
1070
|
"input",
|
|
938
1071
|
{
|
|
939
1072
|
value: q,
|
|
@@ -960,9 +1093,12 @@ Object.assign(__ds_scope, { Timeline });
|
|
|
960
1093
|
|
|
961
1094
|
// components/data/Tooltip.jsx
|
|
962
1095
|
try { (() => {
|
|
1096
|
+
const { makeT, useLang } = __ds_scope;
|
|
963
1097
|
const { cx } = __ds_scope;
|
|
964
|
-
function Tooltip({ label, children, className }) {
|
|
965
|
-
|
|
1098
|
+
function Tooltip({ label, children, lang, className }) {
|
|
1099
|
+
const [ref, L] = useLang(lang);
|
|
1100
|
+
const t = makeT("Tooltip", L);
|
|
1101
|
+
return /* @__PURE__ */ React.createElement("span", { ref, className: cx("cs-tooltip", className) }, children, /* @__PURE__ */ React.createElement("span", { className: "cs-tooltip__bubble", role: "tooltip" }, label ?? t("label")));
|
|
966
1102
|
}
|
|
967
1103
|
Object.assign(__ds_scope, { Tooltip });
|
|
968
1104
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Tooltip.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -970,16 +1106,135 @@ Object.assign(__ds_scope, { Tooltip });
|
|
|
970
1106
|
// components/data/Tree.jsx
|
|
971
1107
|
try { (() => {
|
|
972
1108
|
const { cx } = __ds_scope;
|
|
973
|
-
function
|
|
974
|
-
const
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1109
|
+
function collectVisible(nodes, openMap, out = []) {
|
|
1110
|
+
for (const n of nodes) {
|
|
1111
|
+
out.push(n);
|
|
1112
|
+
const kids = n.children || [];
|
|
1113
|
+
if (kids.length && openMap[n.key]) collectVisible(kids, openMap, out);
|
|
1114
|
+
}
|
|
1115
|
+
return out;
|
|
1116
|
+
}
|
|
1117
|
+
function parentOf(nodes, key, parent = null) {
|
|
1118
|
+
for (const n of nodes) {
|
|
1119
|
+
if (n.key === key) return parent;
|
|
1120
|
+
const kids = n.children || [];
|
|
1121
|
+
if (kids.length) {
|
|
1122
|
+
const p = parentOf(kids, key, n);
|
|
1123
|
+
if (p !== void 0) return p;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
return void 0;
|
|
1127
|
+
}
|
|
1128
|
+
function findNode(nodes, key) {
|
|
1129
|
+
for (const n of nodes) {
|
|
1130
|
+
if (n.key === key) return n;
|
|
1131
|
+
const kids = n.children || [];
|
|
1132
|
+
if (kids.length) {
|
|
1133
|
+
const hit = findNode(kids, key);
|
|
1134
|
+
if (hit) return hit;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
function seedOpen(nodes, defaultOpen, map = {}) {
|
|
1140
|
+
for (const n of nodes) {
|
|
1141
|
+
const kids = n.children || [];
|
|
1142
|
+
if (kids.length) {
|
|
1143
|
+
map[n.key] = !!defaultOpen;
|
|
1144
|
+
seedOpen(kids, defaultOpen, map);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return map;
|
|
980
1148
|
}
|
|
981
1149
|
function Tree({ nodes = [], selected, onSelect, defaultOpen = false, className }) {
|
|
982
|
-
|
|
1150
|
+
const [openMap, setOpenMap] = React.useState(() => seedOpen(nodes, defaultOpen));
|
|
1151
|
+
const [focusKey, setFocusKey] = React.useState(() => {
|
|
1152
|
+
if (selected && findNode(nodes, selected)) return selected;
|
|
1153
|
+
return nodes[0] ? nodes[0].key : null;
|
|
1154
|
+
});
|
|
1155
|
+
const itemRefs = React.useRef({});
|
|
1156
|
+
React.useEffect(() => {
|
|
1157
|
+
setOpenMap((prev) => {
|
|
1158
|
+
const next = seedOpen(nodes, defaultOpen);
|
|
1159
|
+
for (const k of Object.keys(next)) if (k in prev) next[k] = prev[k];
|
|
1160
|
+
return next;
|
|
1161
|
+
});
|
|
1162
|
+
}, [nodes, defaultOpen]);
|
|
1163
|
+
const visible = React.useMemo(() => collectVisible(nodes, openMap), [nodes, openMap]);
|
|
1164
|
+
React.useEffect(() => {
|
|
1165
|
+
if (!visible.length) {
|
|
1166
|
+
setFocusKey(null);
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
if (!focusKey || !visible.some((n) => n.key === focusKey)) setFocusKey(visible[0].key);
|
|
1170
|
+
}, [visible, focusKey]);
|
|
1171
|
+
const toggle = (key) => setOpenMap((m) => ({ ...m, [key]: !m[key] }));
|
|
1172
|
+
const focusItem = (key) => {
|
|
1173
|
+
setFocusKey(key);
|
|
1174
|
+
requestAnimationFrame(() => {
|
|
1175
|
+
const el = itemRefs.current[key];
|
|
1176
|
+
if (el) el.focus();
|
|
1177
|
+
});
|
|
1178
|
+
};
|
|
1179
|
+
const onKeyDown = (e, key) => {
|
|
1180
|
+
if (e.nativeEvent.isComposing || e.keyCode === 229) return;
|
|
1181
|
+
const idx = visible.findIndex((n) => n.key === key);
|
|
1182
|
+
if (idx < 0) return;
|
|
1183
|
+
const node = visible[idx];
|
|
1184
|
+
const kids = node.children || [];
|
|
1185
|
+
const open = !!openMap[key];
|
|
1186
|
+
if (e.key === "ArrowDown") {
|
|
1187
|
+
e.preventDefault();
|
|
1188
|
+
if (idx < visible.length - 1) focusItem(visible[idx + 1].key);
|
|
1189
|
+
} else if (e.key === "ArrowUp") {
|
|
1190
|
+
e.preventDefault();
|
|
1191
|
+
if (idx > 0) focusItem(visible[idx - 1].key);
|
|
1192
|
+
} else if (e.key === "Home") {
|
|
1193
|
+
e.preventDefault();
|
|
1194
|
+
focusItem(visible[0].key);
|
|
1195
|
+
} else if (e.key === "End") {
|
|
1196
|
+
e.preventDefault();
|
|
1197
|
+
focusItem(visible[visible.length - 1].key);
|
|
1198
|
+
} else if (e.key === "ArrowRight") {
|
|
1199
|
+
e.preventDefault();
|
|
1200
|
+
if (kids.length && !open) toggle(key);
|
|
1201
|
+
else if (kids.length && open) focusItem(kids[0].key);
|
|
1202
|
+
} else if (e.key === "ArrowLeft") {
|
|
1203
|
+
e.preventDefault();
|
|
1204
|
+
if (kids.length && open) toggle(key);
|
|
1205
|
+
else {
|
|
1206
|
+
const p = parentOf(nodes, key);
|
|
1207
|
+
if (p) focusItem(p.key);
|
|
1208
|
+
}
|
|
1209
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
1210
|
+
e.preventDefault();
|
|
1211
|
+
onSelect && onSelect(key, node);
|
|
1212
|
+
if (kids.length) toggle(key);
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
const renderNodes = (list, depth) => list.map((n) => {
|
|
1216
|
+
const kids = n.children || [];
|
|
1217
|
+
const open = !!openMap[n.key];
|
|
1218
|
+
return /* @__PURE__ */ React.createElement("li", { key: n.key, role: "treeitem", "aria-expanded": kids.length ? open : void 0, "aria-selected": selected === n.key }, /* @__PURE__ */ React.createElement("span", { className: cx("cs-tree__row", selected === n.key && "is-selected"), style: { paddingInlineStart: depth * 18 + 6 } }, kids.length ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-tree__twist", "aria-hidden": "true", tabIndex: -1, onClick: () => toggle(n.key) }, open ? "▾" : "▸") : /* @__PURE__ */ React.createElement("span", { className: "cs-tree__twist", "aria-hidden": "true" }), /* @__PURE__ */ React.createElement(
|
|
1219
|
+
"button",
|
|
1220
|
+
{
|
|
1221
|
+
type: "button",
|
|
1222
|
+
className: "cs-tree__label",
|
|
1223
|
+
tabIndex: focusKey === n.key ? 0 : -1,
|
|
1224
|
+
ref: (el) => {
|
|
1225
|
+
itemRefs.current[n.key] = el;
|
|
1226
|
+
},
|
|
1227
|
+
onClick: () => {
|
|
1228
|
+
onSelect && onSelect(n.key, n);
|
|
1229
|
+
if (kids.length) toggle(n.key);
|
|
1230
|
+
},
|
|
1231
|
+
onKeyDown: (e) => onKeyDown(e, n.key),
|
|
1232
|
+
onFocus: () => setFocusKey(n.key)
|
|
1233
|
+
},
|
|
1234
|
+
n.label
|
|
1235
|
+
)), kids.length && open ? /* @__PURE__ */ React.createElement("ul", { role: "group" }, renderNodes(kids, depth + 1)) : null);
|
|
1236
|
+
});
|
|
1237
|
+
return /* @__PURE__ */ React.createElement("ul", { role: "tree", className: cx("cs-tree", className) }, renderNodes(nodes, 0));
|
|
983
1238
|
}
|
|
984
1239
|
Object.assign(__ds_scope, { Tree });
|
|
985
1240
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Tree.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -988,10 +1243,17 @@ Object.assign(__ds_scope, { Tree });
|
|
|
988
1243
|
try { (() => {
|
|
989
1244
|
const { cx } = __ds_scope;
|
|
990
1245
|
function Watermark({ text = "CyberSkill", opacity = 0.09, gap = 140, rotate = -22, children, className }) {
|
|
1246
|
+
const host = React.useRef(null);
|
|
1247
|
+
const [fill, setFill] = React.useState("#45210E");
|
|
1248
|
+
React.useLayoutEffect(() => {
|
|
1249
|
+
if (!host.current || typeof getComputedStyle === "undefined") return;
|
|
1250
|
+
const c = getComputedStyle(host.current).getPropertyValue("--cs-color-text-primary").trim();
|
|
1251
|
+
if (c) setFill(c);
|
|
1252
|
+
}, []);
|
|
991
1253
|
const svg = encodeURIComponent(
|
|
992
|
-
`<svg xmlns="http://www.w3.org/2000/svg" width="${gap}" height="${gap}"><text x="50%" y="50%" font-family="Be Vietnam Pro, sans-serif" font-size="14" font-weight="700" fill="
|
|
1254
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${gap}" height="${gap}"><text x="50%" y="50%" font-family="Be Vietnam Pro, sans-serif" font-size="14" font-weight="700" fill="${fill}" fill-opacity="${opacity}" text-anchor="middle" transform="rotate(${rotate} ${gap / 2} ${gap / 2})">${String(text).replace(/&/g, "&").replace(/</g, "<")}</text></svg>`
|
|
993
1255
|
);
|
|
994
|
-
return /* @__PURE__ */ React.createElement("div", { className: cx("cs-watermark", className), style: { position: "relative" } }, children, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", style: { position: "absolute", inset: 0, pointerEvents: "none", backgroundImage: `url("data:image/svg+xml,${svg}")` } }));
|
|
1256
|
+
return /* @__PURE__ */ React.createElement("div", { ref: host, className: cx("cs-watermark", className), style: { position: "relative" } }, children, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", style: { position: "absolute", inset: 0, pointerEvents: "none", backgroundImage: `url("data:image/svg+xml,${svg}")` } }));
|
|
995
1257
|
}
|
|
996
1258
|
Object.assign(__ds_scope, { Watermark });
|
|
997
1259
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Watermark.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -1131,145 +1393,6 @@ function QRCode({ value = "", size = 128, color = "var(--cs-color-text-primary)"
|
|
|
1131
1393
|
Object.assign(__ds_scope, { QRCode });
|
|
1132
1394
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/QRCode.jsx", error: String((e && e.message) || e) }); }
|
|
1133
1395
|
|
|
1134
|
-
// components/datatable/DataGrid.jsx
|
|
1135
|
-
try { (() => {
|
|
1136
|
-
const { makeT, useLang } = __ds_scope;
|
|
1137
|
-
const { cx } = __ds_scope;
|
|
1138
|
-
function DataGrid({
|
|
1139
|
-
columns = [],
|
|
1140
|
-
rows = [],
|
|
1141
|
-
rowKey = "id",
|
|
1142
|
-
selectable = false,
|
|
1143
|
-
selected = [],
|
|
1144
|
-
onSelect,
|
|
1145
|
-
height = 280,
|
|
1146
|
-
caption,
|
|
1147
|
-
empty,
|
|
1148
|
-
lang,
|
|
1149
|
-
className,
|
|
1150
|
-
filterText,
|
|
1151
|
-
filterKeys,
|
|
1152
|
-
/** When true (or rows exceed virtualThreshold), only paint a window of rows. */
|
|
1153
|
-
virtual = false,
|
|
1154
|
-
/** Row threshold that auto-enables virtualization. Default 80. */
|
|
1155
|
-
virtualThreshold = 80,
|
|
1156
|
-
/** Approximate row height for windowing. Default 36. */
|
|
1157
|
-
rowHeight = 36,
|
|
1158
|
-
/** localStorage key — when set, column key order is persisted across reloads. */
|
|
1159
|
-
persistKey
|
|
1160
|
-
}) {
|
|
1161
|
-
const [sort, setSort] = React.useState(null);
|
|
1162
|
-
const [scrollTop, setScrollTop] = React.useState(0);
|
|
1163
|
-
const [colOrder, setColOrder] = React.useState(() => {
|
|
1164
|
-
if (!persistKey) return null;
|
|
1165
|
-
try {
|
|
1166
|
-
const raw = localStorage.getItem("cs:datagrid:cols:" + persistKey);
|
|
1167
|
-
const arr = raw ? JSON.parse(raw) : null;
|
|
1168
|
-
return Array.isArray(arr) ? arr : null;
|
|
1169
|
-
} catch (e) {
|
|
1170
|
-
return null;
|
|
1171
|
-
}
|
|
1172
|
-
});
|
|
1173
|
-
const [ref, L] = useLang(lang);
|
|
1174
|
-
const t = makeT("DataGrid", L);
|
|
1175
|
-
const orderedColumns = React.useMemo(() => {
|
|
1176
|
-
if (!colOrder || !colOrder.length) return columns;
|
|
1177
|
-
const map = new Map(columns.map((c) => [c.key, c]));
|
|
1178
|
-
const out = [];
|
|
1179
|
-
for (const k of colOrder) if (map.has(k)) {
|
|
1180
|
-
out.push(map.get(k));
|
|
1181
|
-
map.delete(k);
|
|
1182
|
-
}
|
|
1183
|
-
for (const c of map.values()) out.push(c);
|
|
1184
|
-
return out;
|
|
1185
|
-
}, [columns, colOrder]);
|
|
1186
|
-
const filtered = React.useMemo(() => {
|
|
1187
|
-
const q = (filterText == null ? "" : String(filterText)).trim().toLowerCase();
|
|
1188
|
-
if (!q) return rows;
|
|
1189
|
-
const keys = filterKeys && filterKeys.length ? filterKeys : orderedColumns.map((c) => c.key);
|
|
1190
|
-
return rows.filter((r) => keys.some((k) => String(r[k] == null ? "" : r[k]).toLowerCase().includes(q)));
|
|
1191
|
-
}, [rows, filterText, filterKeys, orderedColumns]);
|
|
1192
|
-
const sorted = React.useMemo(() => {
|
|
1193
|
-
if (!sort) return filtered;
|
|
1194
|
-
const col = orderedColumns.find((c) => c.key === sort.key);
|
|
1195
|
-
const val = (r) => col && col.sortValue ? col.sortValue(r) : r[sort.key];
|
|
1196
|
-
return [...filtered].sort((a, b) => {
|
|
1197
|
-
const x = val(a), y = val(b);
|
|
1198
|
-
return (x > y ? 1 : x < y ? -1 : 0) * (sort.dir === "asc" ? 1 : -1);
|
|
1199
|
-
});
|
|
1200
|
-
}, [filtered, sort, orderedColumns]);
|
|
1201
|
-
const useVirtual = virtual || sorted.length >= virtualThreshold;
|
|
1202
|
-
const bodyH = Math.max(0, height - 40);
|
|
1203
|
-
const start = useVirtual ? Math.max(0, Math.floor(scrollTop / rowHeight) - 4) : 0;
|
|
1204
|
-
const visibleCount = useVirtual ? Math.ceil(bodyH / rowHeight) + 8 : sorted.length;
|
|
1205
|
-
const end = useVirtual ? Math.min(sorted.length, start + visibleCount) : sorted.length;
|
|
1206
|
-
const slice = sorted.slice(start, end);
|
|
1207
|
-
const padTop = useVirtual ? start * rowHeight : 0;
|
|
1208
|
-
const padBottom = useVirtual ? Math.max(0, (sorted.length - end) * rowHeight) : 0;
|
|
1209
|
-
const allSel = selectable && filtered.length && filtered.every((r) => selected.includes(r[rowKey]));
|
|
1210
|
-
const toggleAll = () => onSelect && onSelect(allSel ? [] : filtered.map((r) => r[rowKey]));
|
|
1211
|
-
const toggle = (k) => onSelect && onSelect(selected.includes(k) ? selected.filter((x) => x !== k) : [...selected, k]);
|
|
1212
|
-
const pinColumn = (key) => {
|
|
1213
|
-
if (!persistKey) return;
|
|
1214
|
-
const keys = orderedColumns.map((c) => c.key);
|
|
1215
|
-
const next = [key, ...keys.filter((k) => k !== key)];
|
|
1216
|
-
setColOrder(next);
|
|
1217
|
-
try {
|
|
1218
|
-
localStorage.setItem("cs:datagrid:cols:" + persistKey, JSON.stringify(next));
|
|
1219
|
-
} catch (e) {
|
|
1220
|
-
}
|
|
1221
|
-
};
|
|
1222
|
-
return /* @__PURE__ */ React.createElement(
|
|
1223
|
-
"div",
|
|
1224
|
-
{
|
|
1225
|
-
ref,
|
|
1226
|
-
className: cx("cs-datagrid", useVirtual && "cs-datagrid--virtual", className),
|
|
1227
|
-
style: { maxBlockSize: height, overflow: "auto" },
|
|
1228
|
-
onScroll: useVirtual ? (e) => setScrollTop(e.currentTarget.scrollTop) : void 0,
|
|
1229
|
-
"data-virtual": useVirtual ? "true" : "false",
|
|
1230
|
-
"data-row-count": sorted.length
|
|
1231
|
-
},
|
|
1232
|
-
/* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, selectable ? /* @__PURE__ */ React.createElement("th", { scope: "col", className: "cs-datagrid__selcol" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", "aria-label": t("selectAll"), checked: !!allSel, onChange: toggleAll })) : null, orderedColumns.map((c) => /* @__PURE__ */ React.createElement(
|
|
1233
|
-
"th",
|
|
1234
|
-
{
|
|
1235
|
-
key: c.key,
|
|
1236
|
-
scope: "col",
|
|
1237
|
-
className: c.pinned ? "cs-datagrid__pinned" : void 0,
|
|
1238
|
-
style: c.pinned ? { position: "sticky", insetInlineStart: 0, zIndex: 1, background: "var(--cs-color-surface-panel)" } : void 0,
|
|
1239
|
-
"aria-sort": sort && sort.key === c.key ? sort.dir === "asc" ? "ascending" : "descending" : void 0
|
|
1240
|
-
},
|
|
1241
|
-
c.sortable ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-datagrid__sort", onClick: () => setSort((s) => !s || s.key !== c.key ? { key: c.key, dir: "asc" } : s.dir === "asc" ? { key: c.key, dir: "desc" } : null) }, c.header, /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, sort && sort.key === c.key ? sort.dir === "asc" ? " ▲" : " ▼" : " ↕")) : c.header,
|
|
1242
|
-
persistKey ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-datagrid__pin", title: "Pin column first", onClick: () => pinColumn(c.key), "aria-label": "Pin " + c.key }, "⟂") : null
|
|
1243
|
-
)))), /* @__PURE__ */ React.createElement("tbody", null, sorted.length === 0 ? /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), className: "cs-table__empty" }, empty != null ? empty : t("empty"))) : /* @__PURE__ */ React.createElement(React.Fragment, null, padTop > 0 ? /* @__PURE__ */ React.createElement("tr", { "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), style: { height: padTop, padding: 0, border: 0 } })) : null, slice.map((r, i) => {
|
|
1244
|
-
const k = r[rowKey] != null ? r[rowKey] : start + i;
|
|
1245
|
-
return /* @__PURE__ */ React.createElement("tr", { key: k, className: selected.includes(k) ? "is-selected" : void 0, style: useVirtual ? { height: rowHeight } : void 0 }, selectable ? /* @__PURE__ */ React.createElement("td", { className: "cs-datagrid__selcol" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", "aria-label": t("selectRow"), checked: selected.includes(k), onChange: () => toggle(k) })) : null, orderedColumns.map((c) => /* @__PURE__ */ React.createElement("td", { key: c.key, className: c.pinned ? "cs-datagrid__pinned" : void 0, style: c.pinned ? { position: "sticky", insetInlineStart: 0, background: "var(--cs-color-surface-panel)" } : void 0 }, c.render ? c.render(r) : r[c.key])));
|
|
1246
|
-
}), padBottom > 0 ? /* @__PURE__ */ React.createElement("tr", { "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), style: { height: padBottom, padding: 0, border: 0 } })) : null)))
|
|
1247
|
-
);
|
|
1248
|
-
}
|
|
1249
|
-
Object.assign(__ds_scope, { DataGrid });
|
|
1250
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/datatable/DataGrid.jsx", error: String((e && e.message) || e) }); }
|
|
1251
|
-
|
|
1252
|
-
// components/datatable/DataTable.jsx
|
|
1253
|
-
try { (() => {
|
|
1254
|
-
const { makeT, useLang } = __ds_scope;
|
|
1255
|
-
const { cx } = __ds_scope;
|
|
1256
|
-
function DataTable({
|
|
1257
|
-
caption,
|
|
1258
|
-
columns,
|
|
1259
|
-
rows,
|
|
1260
|
-
rowKey = "id",
|
|
1261
|
-
emptyState,
|
|
1262
|
-
lang,
|
|
1263
|
-
className
|
|
1264
|
-
}) {
|
|
1265
|
-
const normalized = Array.isArray(rows) ? rows : [];
|
|
1266
|
-
const [ref, L] = useLang(lang);
|
|
1267
|
-
const es = emptyState != null ? emptyState : makeT("DataTable", L)("empty");
|
|
1268
|
-
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-table-wrap", className) }, /* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, columns.map((c) => /* @__PURE__ */ React.createElement("th", { key: c.key, scope: "col" }, c.header)))), /* @__PURE__ */ React.createElement("tbody", null, normalized.length === 0 ? /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: columns.length, className: "cs-table__empty" }, es)) : normalized.map((row, i) => /* @__PURE__ */ React.createElement("tr", { key: row[rowKey] ?? i }, columns.map((c) => /* @__PURE__ */ React.createElement("td", { key: c.key }, c.render ? c.render(row) : row[c.key])))))));
|
|
1269
|
-
}
|
|
1270
|
-
Object.assign(__ds_scope, { DataTable });
|
|
1271
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/datatable/DataTable.jsx", error: String((e && e.message) || e) }); }
|
|
1272
|
-
|
|
1273
1396
|
// components/datatable/TreeTable.jsx
|
|
1274
1397
|
try { (() => {
|
|
1275
1398
|
const { makeT, useLang } = __ds_scope;
|
|
@@ -1290,6 +1413,7 @@ Object.assign(__ds_scope, { TreeTable });
|
|
|
1290
1413
|
|
|
1291
1414
|
// components/feedback/Alert.jsx
|
|
1292
1415
|
try { (() => {
|
|
1416
|
+
const { makeT, useLang } = __ds_scope;
|
|
1293
1417
|
const { cx } = __ds_scope;
|
|
1294
1418
|
const PATHS = {
|
|
1295
1419
|
info: "M12 8h.01M11 12h1v4h1 M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z",
|
|
@@ -1300,8 +1424,10 @@ const PATHS = {
|
|
|
1300
1424
|
function DefaultIcon({ variant }) {
|
|
1301
1425
|
return /* @__PURE__ */ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: PATHS[variant] || PATHS.info }));
|
|
1302
1426
|
}
|
|
1303
|
-
function Alert({ variant = "info", title, icon, children, className, ...props }) {
|
|
1304
|
-
|
|
1427
|
+
function Alert({ variant = "info", title, icon, children, onDismiss, lang, className, ...props }) {
|
|
1428
|
+
const [ref, L] = useLang(lang);
|
|
1429
|
+
const t = makeT("Alert", L);
|
|
1430
|
+
return /* @__PURE__ */ React.createElement("div", { ref, role: "status", className: cx("cs-alert", `cs-alert--${variant}`, className), ...props }, /* @__PURE__ */ React.createElement("span", { className: "cs-alert__icon" }, icon ?? /* @__PURE__ */ React.createElement(DefaultIcon, { variant })), /* @__PURE__ */ React.createElement("div", null, title ? /* @__PURE__ */ React.createElement("p", { className: "cs-alert__title" }, title) : null, children ? /* @__PURE__ */ React.createElement("div", { className: "cs-alert__body" }, children) : null), onDismiss ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-alert__dismiss", "aria-label": t("dismiss"), onClick: onDismiss }, "×") : null);
|
|
1305
1431
|
}
|
|
1306
1432
|
Object.assign(__ds_scope, { Alert });
|
|
1307
1433
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/Alert.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -1317,27 +1443,36 @@ Object.assign(__ds_scope, { Badge });
|
|
|
1317
1443
|
|
|
1318
1444
|
// components/feedback/EmptyState.jsx
|
|
1319
1445
|
try { (() => {
|
|
1446
|
+
const { makeT, useLang } = __ds_scope;
|
|
1320
1447
|
const { cx } = __ds_scope;
|
|
1321
|
-
function EmptyState({ icon, title, children, actions, className }) {
|
|
1322
|
-
|
|
1448
|
+
function EmptyState({ icon, title, children, actions, lang, className }) {
|
|
1449
|
+
const [ref, L] = useLang(lang);
|
|
1450
|
+
const t = makeT("EmptyState", L);
|
|
1451
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-empty", className) }, /* @__PURE__ */ React.createElement("span", { className: "cs-empty__icon" }, icon ?? /* @__PURE__ */ React.createElement("svg", { width: "26", height: "26", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: "M3 8l2-4h14l2 4M3 8v10a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V8M3 8h6l1 3h4l1-3h6" }))), /* @__PURE__ */ React.createElement("div", { className: "cs-empty__title" }, title ?? t("title")), children ? /* @__PURE__ */ React.createElement("div", { className: "cs-empty__body" }, children) : null, actions ? /* @__PURE__ */ React.createElement("div", { className: "cs-empty__actions" }, actions) : null);
|
|
1323
1452
|
}
|
|
1324
1453
|
Object.assign(__ds_scope, { EmptyState });
|
|
1325
1454
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/EmptyState.jsx", error: String((e && e.message) || e) }); }
|
|
1326
1455
|
|
|
1327
1456
|
// components/feedback/ProgressBar.jsx
|
|
1328
1457
|
try { (() => {
|
|
1458
|
+
const { makeT, useLang } = __ds_scope;
|
|
1329
1459
|
const { cx } = __ds_scope;
|
|
1330
|
-
function ProgressBar({ value = 0, max = 100, variant, label, className }) {
|
|
1331
|
-
const
|
|
1460
|
+
function ProgressBar({ value = 0, max = 100, variant, label, lang, className }) {
|
|
1461
|
+
const safeMax = max > 0 ? max : 100;
|
|
1462
|
+
const clamped = Math.max(0, Math.min(safeMax, Number(value) || 0));
|
|
1463
|
+
const pct = Math.max(0, Math.min(100, clamped / safeMax * 100));
|
|
1464
|
+
const [ref, L] = useLang(lang);
|
|
1465
|
+
const t = makeT("ProgressBar", L);
|
|
1332
1466
|
return /* @__PURE__ */ React.createElement(
|
|
1333
1467
|
"div",
|
|
1334
1468
|
{
|
|
1469
|
+
ref,
|
|
1335
1470
|
className: cx("cs-progress", variant && `cs-progress--${variant}`, className),
|
|
1336
1471
|
role: "progressbar",
|
|
1337
|
-
"aria-valuenow": Math.round(
|
|
1472
|
+
"aria-valuenow": Math.round(clamped),
|
|
1338
1473
|
"aria-valuemin": 0,
|
|
1339
|
-
"aria-valuemax":
|
|
1340
|
-
"aria-label": label
|
|
1474
|
+
"aria-valuemax": safeMax,
|
|
1475
|
+
"aria-label": label ?? t("label")
|
|
1341
1476
|
},
|
|
1342
1477
|
/* @__PURE__ */ React.createElement("span", { className: "cs-progress__fill", style: { inlineSize: pct + "%", width: pct + "%" } })
|
|
1343
1478
|
);
|
|
@@ -1383,6 +1518,41 @@ function Skeleton({ variant = "block", width, height, lines, radius, className,
|
|
|
1383
1518
|
Object.assign(__ds_scope, { Skeleton });
|
|
1384
1519
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/Skeleton.jsx", error: String((e && e.message) || e) }); }
|
|
1385
1520
|
|
|
1521
|
+
// components/datatable/DataTable.jsx
|
|
1522
|
+
try { (() => {
|
|
1523
|
+
const { makeT, useLang } = __ds_scope;
|
|
1524
|
+
const { Skeleton } = __ds_scope;
|
|
1525
|
+
const { Result } = __ds_scope;
|
|
1526
|
+
const { cx } = __ds_scope;
|
|
1527
|
+
function DataTable({
|
|
1528
|
+
caption,
|
|
1529
|
+
columns,
|
|
1530
|
+
rows,
|
|
1531
|
+
rowKey = "id",
|
|
1532
|
+
emptyState,
|
|
1533
|
+
state = "idle",
|
|
1534
|
+
errorState,
|
|
1535
|
+
loadingState,
|
|
1536
|
+
loadingRows = 5,
|
|
1537
|
+
lang,
|
|
1538
|
+
className
|
|
1539
|
+
}) {
|
|
1540
|
+
const normalized = Array.isArray(rows) ? rows : [];
|
|
1541
|
+
const [ref, L] = useLang(lang);
|
|
1542
|
+
const t = makeT("DataTable", L);
|
|
1543
|
+
const es = emptyState != null ? emptyState : t("empty");
|
|
1544
|
+
const colCount = Math.max(1, columns && columns.length || 1);
|
|
1545
|
+
if (state === "loading") {
|
|
1546
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-table-wrap", className), "aria-busy": "true" }, /* @__PURE__ */ React.createElement("span", { className: "cs-sr-only" }, t("loading")), loadingState != null ? loadingState : /* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, (columns || []).map((c) => /* @__PURE__ */ React.createElement("th", { key: c.key, scope: "col" }, c.header)))), /* @__PURE__ */ React.createElement("tbody", { "aria-hidden": "true" }, Array.from({ length: Math.max(1, loadingRows) }).map((_, i) => /* @__PURE__ */ React.createElement("tr", { key: i }, Array.from({ length: colCount }).map((__, j) => /* @__PURE__ */ React.createElement("td", { key: j }, /* @__PURE__ */ React.createElement(Skeleton, { lines: 1 }))))))));
|
|
1547
|
+
}
|
|
1548
|
+
if (state === "error") {
|
|
1549
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-table-wrap", "cs-table-wrap--status", className), role: "alert" }, errorState != null ? errorState : /* @__PURE__ */ React.createElement(Result, { status: "error", lang: L, title: t("error") }, t("errorHint")));
|
|
1550
|
+
}
|
|
1551
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-table-wrap", className) }, /* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, (columns || []).map((c) => /* @__PURE__ */ React.createElement("th", { key: c.key, scope: "col" }, c.header)))), /* @__PURE__ */ React.createElement("tbody", null, normalized.length === 0 ? /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: colCount, className: "cs-table__empty" }, es)) : normalized.map((row, i) => /* @__PURE__ */ React.createElement("tr", { key: row[rowKey] ?? i }, (columns || []).map((c) => /* @__PURE__ */ React.createElement("td", { key: c.key }, c.render ? c.render(row) : row[c.key])))))));
|
|
1552
|
+
}
|
|
1553
|
+
Object.assign(__ds_scope, { DataTable });
|
|
1554
|
+
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/datatable/DataTable.jsx", error: String((e && e.message) || e) }); }
|
|
1555
|
+
|
|
1386
1556
|
// components/feedback/Spinner.jsx
|
|
1387
1557
|
try { (() => {
|
|
1388
1558
|
const { makeT, useLang } = __ds_scope;
|
|
@@ -1420,13 +1590,21 @@ Object.assign(__ds_scope, { Tag });
|
|
|
1420
1590
|
try { (() => {
|
|
1421
1591
|
const { makeT, useLang } = __ds_scope;
|
|
1422
1592
|
const { cx } = __ds_scope;
|
|
1593
|
+
const PATHS = {
|
|
1594
|
+
default: "M12 8h.01M11 12h1v4h1 M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z",
|
|
1595
|
+
success: "M4 12.5l5 5 11-11",
|
|
1596
|
+
danger: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z M9 9l6 6 M15 9l-6 6"
|
|
1597
|
+
};
|
|
1598
|
+
function DefaultIcon({ variant }) {
|
|
1599
|
+
return /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: PATHS[variant] || PATHS.default }));
|
|
1600
|
+
}
|
|
1423
1601
|
function ToastStack({ children, lang, className }) {
|
|
1424
1602
|
const [ref, L] = useLang(lang);
|
|
1425
1603
|
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-toast-stack", className), role: "region", "aria-label": makeT("Toast", L)("notifications") }, children);
|
|
1426
1604
|
}
|
|
1427
1605
|
function Toast({ variant = "default", title, icon, onClose, lang, children, className, ...props }) {
|
|
1428
1606
|
const [ref, L] = useLang(lang);
|
|
1429
|
-
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-toast", `cs-toast--${variant}`, className), role: "status", ...props }, /* @__PURE__ */ React.createElement("span", { className: "cs-toast__icon", "aria-hidden": "true" }, icon ?? /* @__PURE__ */ React.createElement(
|
|
1607
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-toast", `cs-toast--${variant}`, className), role: "status", ...props }, /* @__PURE__ */ React.createElement("span", { className: "cs-toast__icon", "aria-hidden": "true" }, icon ?? /* @__PURE__ */ React.createElement(DefaultIcon, { variant })), /* @__PURE__ */ React.createElement("div", null, title ? /* @__PURE__ */ React.createElement("div", { className: "cs-toast__title" }, title) : null, children ? /* @__PURE__ */ React.createElement("div", { className: "cs-toast__body" }, children) : null), onClose ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-toast__close", "aria-label": makeT("Toast", L)("dismiss"), onClick: onClose }, /* @__PURE__ */ React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 6l12 12M18 6L6 18" }))) : null);
|
|
1430
1608
|
}
|
|
1431
1609
|
Object.assign(__ds_scope, { ToastStack, Toast });
|
|
1432
1610
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/Toast.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -1475,7 +1653,8 @@ Object.assign(__ds_scope, { Calendar });
|
|
|
1475
1653
|
try { (() => {
|
|
1476
1654
|
const { makeT, useLang } = __ds_scope;
|
|
1477
1655
|
const { cx } = __ds_scope;
|
|
1478
|
-
|
|
1656
|
+
const EMPTY_PATH = [];
|
|
1657
|
+
function Cascader({ nodes = [], value = EMPTY_PATH, onChange, placeholder, label, disabled = false, lang, className }) {
|
|
1479
1658
|
const [open, setOpen] = React.useState(false);
|
|
1480
1659
|
const [path, setPath] = React.useState(value);
|
|
1481
1660
|
const wrap = React.useRef(null);
|
|
@@ -1483,24 +1662,60 @@ function Cascader({ nodes = [], value = [], onChange, placeholder, label, disabl
|
|
|
1483
1662
|
const [ref, L] = useLang(lang);
|
|
1484
1663
|
const t = makeT("Cascader", L);
|
|
1485
1664
|
const ph = placeholder != null ? placeholder : t("placeholder");
|
|
1665
|
+
const valueKey = Array.isArray(value) ? value.join("\0") : "";
|
|
1486
1666
|
React.useEffect(() => {
|
|
1487
|
-
setPath(value);
|
|
1488
|
-
}, [
|
|
1667
|
+
setPath(Array.isArray(value) ? value : EMPTY_PATH);
|
|
1668
|
+
}, [valueKey]);
|
|
1489
1669
|
React.useEffect(() => {
|
|
1490
1670
|
if (!open) return;
|
|
1491
1671
|
const d = (e) => {
|
|
1492
1672
|
if (wrap.current && !wrap.current.contains(e.target)) setOpen(false);
|
|
1493
1673
|
};
|
|
1494
1674
|
const k = (e) => {
|
|
1495
|
-
if (e.key === "Escape")
|
|
1496
|
-
|
|
1675
|
+
if (e.key === "Escape") {
|
|
1676
|
+
setOpen(false);
|
|
1677
|
+
const field = wrap.current && wrap.current.querySelector(".cs-treeselect__field");
|
|
1678
|
+
field && field.focus && field.focus();
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
if (!wrap.current) return;
|
|
1682
|
+
const opts = [...wrap.current.querySelectorAll('[role="option"]')];
|
|
1683
|
+
if (!opts.length) return;
|
|
1684
|
+
const active = document.activeElement;
|
|
1685
|
+
const idx = opts.indexOf(active);
|
|
1686
|
+
if (e.key === "ArrowDown") {
|
|
1687
|
+
e.preventDefault();
|
|
1688
|
+
(opts[idx < 0 ? 0 : Math.min(opts.length - 1, idx + 1)] || opts[0]).focus();
|
|
1689
|
+
} else if (e.key === "ArrowUp") {
|
|
1690
|
+
e.preventDefault();
|
|
1691
|
+
(opts[idx < 0 ? opts.length - 1 : Math.max(0, idx - 1)] || opts[0]).focus();
|
|
1692
|
+
} else if (e.key === "Home") {
|
|
1693
|
+
e.preventDefault();
|
|
1694
|
+
opts[0].focus();
|
|
1695
|
+
} else if (e.key === "End") {
|
|
1696
|
+
e.preventDefault();
|
|
1697
|
+
opts[opts.length - 1].focus();
|
|
1698
|
+
} else if (e.key === "ArrowRight" && idx >= 0) {
|
|
1699
|
+
active.click && active.click();
|
|
1700
|
+
} else if (e.key === "ArrowLeft") {
|
|
1701
|
+
e.preventDefault();
|
|
1702
|
+
if (path.length) {
|
|
1703
|
+
const next = path.slice(0, -1);
|
|
1704
|
+
setPath(next);
|
|
1705
|
+
} else {
|
|
1706
|
+
setOpen(false);
|
|
1707
|
+
const field = wrap.current.querySelector(".cs-treeselect__field");
|
|
1708
|
+
field && field.focus && field.focus();
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1497
1712
|
document.addEventListener("mousedown", d);
|
|
1498
1713
|
document.addEventListener("keydown", k);
|
|
1499
1714
|
return () => {
|
|
1500
1715
|
document.removeEventListener("mousedown", d);
|
|
1501
1716
|
document.removeEventListener("keydown", k);
|
|
1502
1717
|
};
|
|
1503
|
-
}, [open]);
|
|
1718
|
+
}, [open, path]);
|
|
1504
1719
|
const cols = [];
|
|
1505
1720
|
let level = nodes;
|
|
1506
1721
|
for (let i = 0; level && level.length; i++) {
|
|
@@ -1521,7 +1736,27 @@ function Cascader({ nodes = [], value = [], onChange, placeholder, label, disabl
|
|
|
1521
1736
|
return /* @__PURE__ */ React.createElement("div", { ref: (el) => {
|
|
1522
1737
|
wrap.current = el;
|
|
1523
1738
|
ref.current = el;
|
|
1524
|
-
}, className: cx("cs-cascader", className) }, /* @__PURE__ */ React.createElement(
|
|
1739
|
+
}, className: cx("cs-cascader", className) }, /* @__PURE__ */ React.createElement(
|
|
1740
|
+
"button",
|
|
1741
|
+
{
|
|
1742
|
+
type: "button",
|
|
1743
|
+
id: fieldId,
|
|
1744
|
+
className: "cs-treeselect__field",
|
|
1745
|
+
disabled,
|
|
1746
|
+
"aria-haspopup": "listbox",
|
|
1747
|
+
"aria-expanded": open,
|
|
1748
|
+
"aria-label": label,
|
|
1749
|
+
onClick: () => setOpen((o) => !o),
|
|
1750
|
+
onKeyDown: (e) => {
|
|
1751
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
1752
|
+
e.preventDefault();
|
|
1753
|
+
setOpen(true);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
},
|
|
1757
|
+
/* @__PURE__ */ React.createElement("span", { className: labels.length ? void 0 : "ph" }, labels.length ? labels.join(" / ") : ph),
|
|
1758
|
+
/* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "▾")
|
|
1759
|
+
), open ? /* @__PURE__ */ React.createElement("div", { className: "cs-cascader__pop" }, cols.map((col, i) => /* @__PURE__ */ React.createElement("ul", { key: i, role: "listbox", "aria-labelledby": fieldId, className: "cs-cascader__col" }, col.map((n) => (
|
|
1525
1760
|
// role="none" — the <li> wrapper must not sit between listbox and option.
|
|
1526
1761
|
/* @__PURE__ */ React.createElement("li", { key: n.key, role: "none" }, /* @__PURE__ */ React.createElement(
|
|
1527
1762
|
"button",
|
|
@@ -1530,6 +1765,7 @@ function Cascader({ nodes = [], value = [], onChange, placeholder, label, disabl
|
|
|
1530
1765
|
role: "option",
|
|
1531
1766
|
"aria-selected": path[i] === n.key,
|
|
1532
1767
|
className: cx("cs-cascader__opt", path[i] === n.key && "on"),
|
|
1768
|
+
"data-cs-focusable": "",
|
|
1533
1769
|
onClick: () => {
|
|
1534
1770
|
const next = [...path.slice(0, i), n.key];
|
|
1535
1771
|
setPath(next);
|
|
@@ -1759,8 +1995,24 @@ Object.assign(__ds_scope, { DatePicker });
|
|
|
1759
1995
|
try { (() => {
|
|
1760
1996
|
const { makeT, useLang } = __ds_scope;
|
|
1761
1997
|
const { cx } = __ds_scope;
|
|
1998
|
+
function sanitizeHtml(html) {
|
|
1999
|
+
if (html == null || html === "") return "";
|
|
2000
|
+
if (typeof document === "undefined") {
|
|
2001
|
+
return String(html).replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "").replace(/on\w+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, "").replace(/javascript:/gi, "");
|
|
2002
|
+
}
|
|
2003
|
+
const tpl = document.createElement("template");
|
|
2004
|
+
tpl.innerHTML = String(html);
|
|
2005
|
+
tpl.content.querySelectorAll("script,style,iframe,object,embed,link").forEach((n) => n.remove());
|
|
2006
|
+
tpl.content.querySelectorAll("*").forEach((n) => {
|
|
2007
|
+
[...n.attributes].forEach((a) => {
|
|
2008
|
+
if (/^on/i.test(a.name) || /javascript:/i.test(a.value)) n.removeAttribute(a.name);
|
|
2009
|
+
});
|
|
2010
|
+
});
|
|
2011
|
+
return tpl.innerHTML;
|
|
2012
|
+
}
|
|
1762
2013
|
function Editor({ defaultValue = "", onChange, minHeight = 120, lang, className }) {
|
|
1763
2014
|
const box = React.useRef(null);
|
|
2015
|
+
const seeded = React.useRef(false);
|
|
1764
2016
|
const [ref, L] = useLang(lang);
|
|
1765
2017
|
const t = makeT("Editor", L);
|
|
1766
2018
|
const cmd = (c) => {
|
|
@@ -1771,6 +2023,11 @@ function Editor({ defaultValue = "", onChange, minHeight = 120, lang, className
|
|
|
1771
2023
|
const emit = () => {
|
|
1772
2024
|
onChange && box.current && onChange(box.current.innerHTML);
|
|
1773
2025
|
};
|
|
2026
|
+
React.useLayoutEffect(() => {
|
|
2027
|
+
if (!box.current || seeded.current) return;
|
|
2028
|
+
box.current.innerHTML = sanitizeHtml(defaultValue);
|
|
2029
|
+
seeded.current = true;
|
|
2030
|
+
}, [defaultValue]);
|
|
1774
2031
|
const B = ({ c, label, children }) => /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-toolbar__btn", "aria-label": label, onMouseDown: (e) => {
|
|
1775
2032
|
e.preventDefault();
|
|
1776
2033
|
cmd(c);
|
|
@@ -1786,8 +2043,7 @@ function Editor({ defaultValue = "", onChange, minHeight = 120, lang, className
|
|
|
1786
2043
|
"aria-multiline": "true",
|
|
1787
2044
|
"aria-label": t("area"),
|
|
1788
2045
|
style: { minHeight },
|
|
1789
|
-
onInput: emit
|
|
1790
|
-
dangerouslySetInnerHTML: { __html: defaultValue }
|
|
2046
|
+
onInput: emit
|
|
1791
2047
|
}
|
|
1792
2048
|
));
|
|
1793
2049
|
}
|
|
@@ -1800,6 +2056,7 @@ const { makeT, useLang } = __ds_scope;
|
|
|
1800
2056
|
const { cx } = __ds_scope;
|
|
1801
2057
|
function FileUpload({ title, hint, accept, multiple = false, onFiles, icon, lang, className }) {
|
|
1802
2058
|
const [drag, setDrag] = React.useState(false);
|
|
2059
|
+
const inputRef = React.useRef(null);
|
|
1803
2060
|
const pick = (files) => {
|
|
1804
2061
|
if (files && files.length && onFiles) onFiles(Array.from(files));
|
|
1805
2062
|
};
|
|
@@ -1807,8 +2064,9 @@ function FileUpload({ title, hint, accept, multiple = false, onFiles, icon, lang
|
|
|
1807
2064
|
const t = makeT("FileUpload", L);
|
|
1808
2065
|
const tt = title != null ? title : t("title");
|
|
1809
2066
|
const hh = hint != null ? hint : t("hint");
|
|
2067
|
+
const id = React.useId();
|
|
1810
2068
|
return /* @__PURE__ */ React.createElement(
|
|
1811
|
-
"
|
|
2069
|
+
"div",
|
|
1812
2070
|
{
|
|
1813
2071
|
ref,
|
|
1814
2072
|
className: cx("cs-dropzone", drag && "is-dragging", className),
|
|
@@ -1821,12 +2079,26 @@ function FileUpload({ title, hint, accept, multiple = false, onFiles, icon, lang
|
|
|
1821
2079
|
e.preventDefault();
|
|
1822
2080
|
setDrag(false);
|
|
1823
2081
|
pick(e.dataTransfer.files);
|
|
1824
|
-
}
|
|
2082
|
+
},
|
|
2083
|
+
onClick: () => inputRef.current && inputRef.current.click()
|
|
1825
2084
|
},
|
|
1826
|
-
/* @__PURE__ */ React.createElement("span", { className: "cs-dropzone__icon" }, icon ?? /* @__PURE__ */ React.createElement("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round"
|
|
1827
|
-
/* @__PURE__ */ React.createElement("
|
|
1828
|
-
/* @__PURE__ */ React.createElement("span", { className: "cs-dropzone__hint" }, hh),
|
|
1829
|
-
/* @__PURE__ */ React.createElement(
|
|
2085
|
+
/* @__PURE__ */ React.createElement("span", { className: "cs-dropzone__icon", "aria-hidden": "true" }, icon ?? /* @__PURE__ */ React.createElement("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M12 16V4M7 9l5-5 5 5" }), /* @__PURE__ */ React.createElement("path", { d: "M5 16v3a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3" }))),
|
|
2086
|
+
/* @__PURE__ */ React.createElement("label", { className: "cs-dropzone__title", htmlFor: id }, tt),
|
|
2087
|
+
/* @__PURE__ */ React.createElement("span", { className: "cs-dropzone__hint", id: id + "-hint" }, hh),
|
|
2088
|
+
/* @__PURE__ */ React.createElement(
|
|
2089
|
+
"input",
|
|
2090
|
+
{
|
|
2091
|
+
id,
|
|
2092
|
+
ref: inputRef,
|
|
2093
|
+
type: "file",
|
|
2094
|
+
accept,
|
|
2095
|
+
multiple,
|
|
2096
|
+
className: "cs-sr-only",
|
|
2097
|
+
"aria-describedby": id + "-hint",
|
|
2098
|
+
onChange: (e) => pick(e.target.files),
|
|
2099
|
+
onClick: (e) => e.stopPropagation()
|
|
2100
|
+
}
|
|
2101
|
+
)
|
|
1830
2102
|
);
|
|
1831
2103
|
}
|
|
1832
2104
|
Object.assign(__ds_scope, { FileUpload });
|
|
@@ -1873,6 +2145,7 @@ function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues, childre
|
|
|
1873
2145
|
const [values, setValues] = React.useState(() => initialValues || {});
|
|
1874
2146
|
const [ruleErrors, setRuleErrors] = React.useState({});
|
|
1875
2147
|
const [pending, setPending] = React.useState(false);
|
|
2148
|
+
const pendingRef = React.useRef(false);
|
|
1876
2149
|
const setValue = React.useCallback((name, v) => {
|
|
1877
2150
|
setValues((s) => String(name).includes(".") ? setPath(s, name, v) : { ...s, [name]: v });
|
|
1878
2151
|
setRuleErrors((e) => e[name] ? { ...e, [name]: void 0 } : e);
|
|
@@ -1919,8 +2192,10 @@ function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues, childre
|
|
|
1919
2192
|
ref,
|
|
1920
2193
|
className: cx("cs-form", pending && "is-pending", className),
|
|
1921
2194
|
noValidate: true,
|
|
2195
|
+
"aria-busy": pending || void 0,
|
|
1922
2196
|
onSubmit: async (e) => {
|
|
1923
2197
|
e.preventDefault();
|
|
2198
|
+
if (pendingRef.current) return;
|
|
1924
2199
|
const fd = new FormData(e.currentTarget);
|
|
1925
2200
|
const v = {};
|
|
1926
2201
|
fd.forEach((val, k) => {
|
|
@@ -1932,6 +2207,7 @@ function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues, childre
|
|
|
1932
2207
|
setRuleErrors(errs);
|
|
1933
2208
|
return;
|
|
1934
2209
|
}
|
|
2210
|
+
pendingRef.current = true;
|
|
1935
2211
|
setPending(true);
|
|
1936
2212
|
try {
|
|
1937
2213
|
const aerrs = await runAsyncRules(v);
|
|
@@ -1939,6 +2215,7 @@ function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues, childre
|
|
|
1939
2215
|
if (Object.keys(aerrs).some((k) => aerrs[k])) return;
|
|
1940
2216
|
onSubmit && onSubmit(v);
|
|
1941
2217
|
} finally {
|
|
2218
|
+
pendingRef.current = false;
|
|
1942
2219
|
setPending(false);
|
|
1943
2220
|
}
|
|
1944
2221
|
}
|
|
@@ -2239,6 +2516,7 @@ Object.assign(__ds_scope, { InputOTP });
|
|
|
2239
2516
|
try { (() => {
|
|
2240
2517
|
const { makeT, useLang } = __ds_scope;
|
|
2241
2518
|
const { cx } = __ds_scope;
|
|
2519
|
+
let mentionsUid = 0;
|
|
2242
2520
|
function Mentions({ value, defaultValue = "", onChange, users = [], placeholder, rows = 3, lang, className }) {
|
|
2243
2521
|
const [inner, setInner] = React.useState(defaultValue);
|
|
2244
2522
|
const val = value != null ? value : inner;
|
|
@@ -2247,23 +2525,80 @@ function Mentions({ value, defaultValue = "", onChange, users = [], placeholder,
|
|
|
2247
2525
|
onChange && onChange(v);
|
|
2248
2526
|
};
|
|
2249
2527
|
const [q, setQ] = React.useState(null);
|
|
2528
|
+
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
2529
|
+
const [listId] = React.useState(() => "cs-mentions-" + ++mentionsUid);
|
|
2250
2530
|
const [ref, L] = useLang(lang);
|
|
2251
2531
|
const t = makeT("Mentions", L);
|
|
2252
2532
|
const ph = placeholder != null ? placeholder : t("placeholder");
|
|
2533
|
+
const hits = q == null ? [] : users.filter((u) => u.toLowerCase().includes(q)).slice(0, 6);
|
|
2534
|
+
const open = hits.length > 0;
|
|
2535
|
+
React.useEffect(() => {
|
|
2536
|
+
setActiveIndex((i) => hits.length ? Math.min(i, hits.length - 1) : 0);
|
|
2537
|
+
}, [q, users]);
|
|
2253
2538
|
const onInput = (v) => {
|
|
2254
2539
|
set(v);
|
|
2255
2540
|
const m = /(^|\s)@(\w*)$/.exec(v);
|
|
2256
|
-
|
|
2541
|
+
const next = m ? m[2].toLowerCase() : null;
|
|
2542
|
+
setQ(next);
|
|
2543
|
+
if (next != null) setActiveIndex(0);
|
|
2257
2544
|
};
|
|
2258
|
-
const hits = q == null ? [] : users.filter((u) => u.toLowerCase().includes(q)).slice(0, 6);
|
|
2259
2545
|
const pick = (u) => {
|
|
2260
2546
|
set(val.replace(/(^|\s)@\w*$/, "$1@" + u + " "));
|
|
2261
2547
|
setQ(null);
|
|
2262
2548
|
};
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2549
|
+
const optionId = (i) => listId + "-opt-" + i;
|
|
2550
|
+
const onKeyDown = (e) => {
|
|
2551
|
+
if (e.nativeEvent.isComposing || e.keyCode === 229) return;
|
|
2552
|
+
if (!open) return;
|
|
2553
|
+
if (e.key === "ArrowDown") {
|
|
2554
|
+
e.preventDefault();
|
|
2555
|
+
setActiveIndex((i) => Math.min(hits.length - 1, i + 1));
|
|
2556
|
+
} else if (e.key === "ArrowUp") {
|
|
2557
|
+
e.preventDefault();
|
|
2558
|
+
setActiveIndex((i) => Math.max(0, i - 1));
|
|
2559
|
+
} else if (e.key === "Enter") {
|
|
2560
|
+
if (hits[activeIndex]) {
|
|
2561
|
+
e.preventDefault();
|
|
2562
|
+
pick(hits[activeIndex]);
|
|
2563
|
+
}
|
|
2564
|
+
} else if (e.key === "Escape") {
|
|
2565
|
+
e.preventDefault();
|
|
2566
|
+
setQ(null);
|
|
2567
|
+
}
|
|
2568
|
+
};
|
|
2569
|
+
return /* @__PURE__ */ React.createElement("span", { ref, className: cx("cs-mentions", className) }, /* @__PURE__ */ React.createElement(
|
|
2570
|
+
"textarea",
|
|
2571
|
+
{
|
|
2572
|
+
className: "cs-field__control",
|
|
2573
|
+
rows,
|
|
2574
|
+
value: val,
|
|
2575
|
+
placeholder: ph,
|
|
2576
|
+
role: "combobox",
|
|
2577
|
+
"aria-autocomplete": "list",
|
|
2578
|
+
"aria-expanded": open,
|
|
2579
|
+
"aria-controls": listId,
|
|
2580
|
+
"aria-activedescendant": open && hits[activeIndex] ? optionId(activeIndex) : void 0,
|
|
2581
|
+
onChange: (e) => onInput(e.target.value),
|
|
2582
|
+
onKeyDown
|
|
2583
|
+
}
|
|
2584
|
+
), open ? /* @__PURE__ */ React.createElement("span", { className: "cs-mentions__pop", role: "listbox", id: listId }, hits.map((u, i) => /* @__PURE__ */ React.createElement(
|
|
2585
|
+
"button",
|
|
2586
|
+
{
|
|
2587
|
+
key: u,
|
|
2588
|
+
type: "button",
|
|
2589
|
+
id: optionId(i),
|
|
2590
|
+
role: "option",
|
|
2591
|
+
"aria-selected": i === activeIndex,
|
|
2592
|
+
className: cx(i === activeIndex && "hl"),
|
|
2593
|
+
onMouseEnter: () => setActiveIndex(i),
|
|
2594
|
+
onMouseDown: (e) => {
|
|
2595
|
+
e.preventDefault();
|
|
2596
|
+
pick(u);
|
|
2597
|
+
}
|
|
2598
|
+
},
|
|
2599
|
+
"@",
|
|
2600
|
+
u
|
|
2601
|
+
))) : null);
|
|
2267
2602
|
}
|
|
2268
2603
|
Object.assign(__ds_scope, { Mentions });
|
|
2269
2604
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Mentions.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -2393,15 +2728,57 @@ const STAR = "M12 2l2.9 6.2 6.6.8-4.9 4.6 1.3 6.5L12 16.9 6.1 20l1.3-6.5L2.5 9l6
|
|
|
2393
2728
|
function Rating({ value, defaultValue = 0, onChange, max = 5, readOnly = false, label, lang, className }) {
|
|
2394
2729
|
const [inner, setInner] = React.useState(defaultValue);
|
|
2395
2730
|
const val = value != null ? value : inner;
|
|
2396
|
-
const
|
|
2731
|
+
const commit = (n) => {
|
|
2397
2732
|
if (readOnly) return;
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
onChange && onChange(v);
|
|
2733
|
+
if (value == null) setInner(n);
|
|
2734
|
+
onChange && onChange(n);
|
|
2401
2735
|
};
|
|
2736
|
+
const set = (n) => commit(n === val ? 0 : n);
|
|
2402
2737
|
const [ref, L] = useLang(lang);
|
|
2403
2738
|
const lbl = label != null ? label : makeT("Rating", L)("label");
|
|
2404
|
-
|
|
2739
|
+
const refs = React.useRef([]);
|
|
2740
|
+
const focusIdx = val > 0 ? Math.min(max, val) - 1 : 0;
|
|
2741
|
+
const move = (from, delta) => {
|
|
2742
|
+
if (readOnly) return;
|
|
2743
|
+
const next = Math.max(0, Math.min(max - 1, from + delta));
|
|
2744
|
+
commit(next + 1);
|
|
2745
|
+
const b = refs.current[next];
|
|
2746
|
+
if (b) b.focus();
|
|
2747
|
+
};
|
|
2748
|
+
const onKeyDown = (e, i) => {
|
|
2749
|
+
if (readOnly || e.nativeEvent.isComposing || e.keyCode === 229) return;
|
|
2750
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
2751
|
+
e.preventDefault();
|
|
2752
|
+
move(i, 1);
|
|
2753
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
2754
|
+
e.preventDefault();
|
|
2755
|
+
move(i, -1);
|
|
2756
|
+
} else if (e.key === "Home") {
|
|
2757
|
+
e.preventDefault();
|
|
2758
|
+
move(i, -i);
|
|
2759
|
+
} else if (e.key === "End") {
|
|
2760
|
+
e.preventDefault();
|
|
2761
|
+
move(i, max - 1 - i);
|
|
2762
|
+
}
|
|
2763
|
+
};
|
|
2764
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-rating", className), role: "radiogroup", "aria-label": lbl + ": " + val + " / " + max, "data-readonly": readOnly ? "true" : void 0 }, Array.from({ length: max }).map((_, i) => /* @__PURE__ */ React.createElement(
|
|
2765
|
+
"button",
|
|
2766
|
+
{
|
|
2767
|
+
key: i,
|
|
2768
|
+
type: "button",
|
|
2769
|
+
role: "radio",
|
|
2770
|
+
"aria-checked": i < val,
|
|
2771
|
+
"aria-label": i + 1 + " / " + max,
|
|
2772
|
+
className: i < val ? "on" : void 0,
|
|
2773
|
+
onClick: () => set(i + 1),
|
|
2774
|
+
tabIndex: readOnly ? -1 : i === focusIdx ? 0 : -1,
|
|
2775
|
+
ref: (el) => {
|
|
2776
|
+
refs.current[i] = el;
|
|
2777
|
+
},
|
|
2778
|
+
onKeyDown: (e) => onKeyDown(e, i)
|
|
2779
|
+
},
|
|
2780
|
+
/* @__PURE__ */ React.createElement("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: i < val ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: STAR }))
|
|
2781
|
+
)));
|
|
2405
2782
|
}
|
|
2406
2783
|
Object.assign(__ds_scope, { Rating });
|
|
2407
2784
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Rating.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -2464,32 +2841,53 @@ Object.assign(__ds_scope, { SegmentedControl });
|
|
|
2464
2841
|
|
|
2465
2842
|
// components/forms/Select.jsx
|
|
2466
2843
|
try { (() => {
|
|
2844
|
+
const { makeT, useLang } = __ds_scope;
|
|
2467
2845
|
const { cx } = __ds_scope;
|
|
2468
|
-
function Select({ id, label, description, error, options, children, disabled = false, className, ...props }) {
|
|
2846
|
+
function Select({ id, label, description, error, options, children, disabled = false, lang, className, placeholder, ...props }) {
|
|
2847
|
+
const [ref, L] = useLang(lang);
|
|
2848
|
+
const t = makeT("Select", L);
|
|
2469
2849
|
const gid = React.useId();
|
|
2470
2850
|
const sid = id ?? gid;
|
|
2471
2851
|
const descId = description ? sid + "-desc" : void 0;
|
|
2472
2852
|
const errId = error ? sid + "-err" : void 0;
|
|
2473
2853
|
const describedBy = [descId, errId].filter(Boolean).join(" ") || void 0;
|
|
2474
|
-
return /* @__PURE__ */ React.createElement("label", { className: cx("cs-field", disabled && "is-disabled", error && "is-invalid", className), htmlFor: sid }, label ? /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, label) : null, description ? /* @__PURE__ */ React.createElement("span", { id: descId, className: "cs-field__description" }, description) : null, /* @__PURE__ */ React.createElement("span", { className: "cs-select" }, /* @__PURE__ */ React.createElement("select", { ...props, id: sid, disabled, "aria-invalid": error ? true : void 0, "aria-describedby": describedBy, className: "cs-field__control" }, options ? options.map((o) => /* @__PURE__ */ React.createElement("option", { key: o.value, value: o.value }, o.label)) : children), /* @__PURE__ */ React.createElement("span", { className: "cs-select__chevron", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 9l6 6 6-6" })))), error ? /* @__PURE__ */ React.createElement("span", { id: errId, className: "cs-field__error", role: "alert" }, error) : null);
|
|
2854
|
+
return /* @__PURE__ */ React.createElement("label", { ref, className: cx("cs-field", disabled && "is-disabled", error && "is-invalid", className), htmlFor: sid }, label ? /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, label) : null, description ? /* @__PURE__ */ React.createElement("span", { id: descId, className: "cs-field__description" }, description) : null, /* @__PURE__ */ React.createElement("span", { className: "cs-select" }, /* @__PURE__ */ React.createElement("select", { ...props, id: sid, disabled, "aria-invalid": error ? true : void 0, "aria-describedby": describedBy, className: "cs-field__control" }, placeholder != null || !children ? /* @__PURE__ */ React.createElement("option", { value: "", disabled: true, hidden: true }, placeholder ?? t("placeholder")) : null, options ? options.map((o) => /* @__PURE__ */ React.createElement("option", { key: o.value, value: o.value }, o.label)) : children), /* @__PURE__ */ React.createElement("span", { className: "cs-select__chevron", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 9l6 6 6-6" })))), error ? /* @__PURE__ */ React.createElement("span", { id: errId, className: "cs-field__error", role: "alert" }, error) : null);
|
|
2475
2855
|
}
|
|
2476
2856
|
Object.assign(__ds_scope, { Select });
|
|
2477
2857
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Select.jsx", error: String((e && e.message) || e) }); }
|
|
2478
2858
|
|
|
2479
2859
|
// components/forms/Slider.jsx
|
|
2480
2860
|
try { (() => {
|
|
2861
|
+
const { makeT, useLang } = __ds_scope;
|
|
2481
2862
|
const { cx } = __ds_scope;
|
|
2482
|
-
function Slider({ className, children, ...props }) {
|
|
2483
|
-
|
|
2863
|
+
function Slider({ className, children, lang, "aria-label": ariaLabel, ...props }) {
|
|
2864
|
+
const [ref, L] = useLang(lang);
|
|
2865
|
+
const t = makeT("Slider", L);
|
|
2866
|
+
return /* @__PURE__ */ React.createElement("input", { ref, type: "range", className: cx("cs-slider", className), "aria-label": ariaLabel ?? t("label"), ...props });
|
|
2484
2867
|
}
|
|
2485
2868
|
Object.assign(__ds_scope, { Slider });
|
|
2486
2869
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Slider.jsx", error: String((e && e.message) || e) }); }
|
|
2487
2870
|
|
|
2488
2871
|
// components/forms/Switch.jsx
|
|
2489
2872
|
try { (() => {
|
|
2873
|
+
const { makeT, useLang } = __ds_scope;
|
|
2490
2874
|
const { cx } = __ds_scope;
|
|
2491
|
-
function Switch({ label, disabled = false, className, children, ...props }) {
|
|
2492
|
-
|
|
2875
|
+
function Switch({ label, disabled = false, className, children, lang, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...props }) {
|
|
2876
|
+
const [ref, L] = useLang(lang);
|
|
2877
|
+
const t = makeT("Switch", L);
|
|
2878
|
+
const named = !!(label || ariaLabel || ariaLabelledby);
|
|
2879
|
+
const fallback = props.checked || props.defaultChecked ? t("on") : t("off");
|
|
2880
|
+
return /* @__PURE__ */ React.createElement("label", { ref, className: cx("cs-switch", disabled && "is-disabled", className) }, /* @__PURE__ */ React.createElement(
|
|
2881
|
+
"input",
|
|
2882
|
+
{
|
|
2883
|
+
type: "checkbox",
|
|
2884
|
+
role: "switch",
|
|
2885
|
+
disabled,
|
|
2886
|
+
"aria-label": ariaLabel ?? (named ? void 0 : fallback),
|
|
2887
|
+
"aria-labelledby": ariaLabelledby,
|
|
2888
|
+
...props
|
|
2889
|
+
}
|
|
2890
|
+
), /* @__PURE__ */ React.createElement("span", { className: "cs-switch__track", "aria-hidden": "true" }), label ? /* @__PURE__ */ React.createElement("span", { className: "cs-switch__label" }, label) : null);
|
|
2493
2891
|
}
|
|
2494
2892
|
Object.assign(__ds_scope, { Switch });
|
|
2495
2893
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Switch.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -2589,8 +2987,10 @@ const CLOCK = /* @__PURE__ */ React.createElement("svg", { width: "16", height:
|
|
|
2589
2987
|
function TimePicker({ value = "09:00", onChange, step = 30, label, disabled = false, lang, className }) {
|
|
2590
2988
|
const [ref, L] = useLang(lang);
|
|
2591
2989
|
const t = makeT("TimePicker", L);
|
|
2990
|
+
const stepMin = Number(step);
|
|
2991
|
+
const safeStep = Number.isFinite(stepMin) && stepMin > 0 ? stepMin : 30;
|
|
2592
2992
|
const opts = [];
|
|
2593
|
-
for (let m = 0; m < 24 * 60; m +=
|
|
2993
|
+
for (let m = 0; m < 24 * 60; m += safeStep) {
|
|
2594
2994
|
opts.push(String(Math.floor(m / 60)).padStart(2, "0") + ":" + String(m % 60).padStart(2, "0"));
|
|
2595
2995
|
}
|
|
2596
2996
|
return /* @__PURE__ */ React.createElement("span", { ref, className: cx("cs-timepicker", className) }, CLOCK, /* @__PURE__ */ React.createElement("select", { "aria-label": label != null ? label : t("label"), disabled, value, onChange: (e) => onChange && onChange(e.target.value) }, opts.map((o) => /* @__PURE__ */ React.createElement("option", { key: o, value: o }, o))));
|
|
@@ -2600,16 +3000,33 @@ Object.assign(__ds_scope, { TimePicker });
|
|
|
2600
3000
|
|
|
2601
3001
|
// components/forms/Toggle.jsx
|
|
2602
3002
|
try { (() => {
|
|
3003
|
+
const { makeT, useLang } = __ds_scope;
|
|
2603
3004
|
const { cx } = __ds_scope;
|
|
2604
|
-
function Toggle({ pressed, defaultPressed = false, onChange, icon, children, disabled = false, className, ...props }) {
|
|
3005
|
+
function Toggle({ pressed, defaultPressed = false, onChange, icon, children, disabled = false, lang, className, "aria-label": ariaLabel, ...props }) {
|
|
2605
3006
|
const [inner, setInner] = React.useState(defaultPressed);
|
|
2606
3007
|
const on = pressed != null ? pressed : inner;
|
|
3008
|
+
const [ref, L] = useLang(lang);
|
|
3009
|
+
const t = makeT("Toggle", L);
|
|
2607
3010
|
const flip = () => {
|
|
2608
3011
|
const v = !on;
|
|
2609
3012
|
if (pressed == null) setInner(v);
|
|
2610
3013
|
onChange && onChange(v);
|
|
2611
3014
|
};
|
|
2612
|
-
return /* @__PURE__ */ React.createElement(
|
|
3015
|
+
return /* @__PURE__ */ React.createElement(
|
|
3016
|
+
"button",
|
|
3017
|
+
{
|
|
3018
|
+
ref,
|
|
3019
|
+
type: "button",
|
|
3020
|
+
className: cx("cs-toggle", className),
|
|
3021
|
+
"aria-pressed": on,
|
|
3022
|
+
"aria-label": ariaLabel ?? (on ? t("pressed") : t("unpressed")),
|
|
3023
|
+
disabled,
|
|
3024
|
+
onClick: flip,
|
|
3025
|
+
...props
|
|
3026
|
+
},
|
|
3027
|
+
icon ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", style: { display: "inline-flex" } }, icon) : null,
|
|
3028
|
+
children
|
|
3029
|
+
);
|
|
2613
3030
|
}
|
|
2614
3031
|
Object.assign(__ds_scope, { Toggle });
|
|
2615
3032
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Toggle.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -2730,7 +3147,8 @@ const CS_ICONS = {
|
|
|
2730
3147
|
edit: { viewBox: "0 0 24 24", els: [["path", { d: "M4 20h4L18.5 9.5a2.12 2.12 0 0 0-3-3L5 17z" }], ["path", { d: "M13.5 6.5l3 3" }]] },
|
|
2731
3148
|
copy: { viewBox: "0 0 24 24", els: [["rect", { x: 8, y: 8, width: 12, height: 12, rx: 2 }], ["path", { d: "M4 16V6a2 2 0 0 1 2-2h10" }]] },
|
|
2732
3149
|
info: { viewBox: "0 0 24 24", els: [["circle", { cx: 12, cy: 12, r: 9 }], ["path", { d: "M12 11v5M12 7.75v.01" }]] },
|
|
2733
|
-
"alert-triangle": { viewBox: "0 0 24 24", els: [["path", { d: "M12 3.5l9 16H3z" }], ["path", { d: "M12 10v4M12 17.5v.01" }]] }
|
|
3150
|
+
"alert-triangle": { viewBox: "0 0 24 24", els: [["path", { d: "M12 3.5l9 16H3z" }], ["path", { d: "M12 10v4M12 17.5v.01" }]] },
|
|
3151
|
+
pin: { viewBox: "0 0 24 24", els: [["path", { d: "M12 21v-7" }], ["path", { d: "M8.5 3.5h7l-1.2 6.5h2.7L12 14.5 6.999 10h2.7z" }]] }
|
|
2734
3152
|
};
|
|
2735
3153
|
function Icon({ name, size = "md", label, className, strokeWidth = 1.75, ...props }) {
|
|
2736
3154
|
const def = CS_ICONS[name] || CS_ICONS.sparkle;
|
|
@@ -2774,16 +3192,26 @@ function Image({ src, alt = "", ratio, preview = false, fallback, lang, classNam
|
|
|
2774
3192
|
document.addEventListener("keydown", k);
|
|
2775
3193
|
return () => document.removeEventListener("keydown", k);
|
|
2776
3194
|
}, [zoom]);
|
|
3195
|
+
const openPreview = () => setZoom(true);
|
|
3196
|
+
const onPreviewKey = (e) => {
|
|
3197
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3198
|
+
e.preventDefault();
|
|
3199
|
+
openPreview();
|
|
3200
|
+
}
|
|
3201
|
+
};
|
|
2777
3202
|
const body = state === "error" ? /* @__PURE__ */ React.createElement("span", { className: "cs-image__fallback" }, fallback || /* @__PURE__ */ React.createElement("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "9", cy: "9", r: "2" }), /* @__PURE__ */ React.createElement("path", { d: "M21 15l-5-5-9 9" }))) : /* @__PURE__ */ React.createElement("img", { ...props, src, alt, onLoad: () => setState("ok"), onError: () => setState("error") });
|
|
3203
|
+
const canPreview = preview && state === "ok";
|
|
2778
3204
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
2779
3205
|
"span",
|
|
2780
3206
|
{
|
|
2781
3207
|
ref,
|
|
2782
|
-
className: cx("cs-image", state === "loading" && "is-loading",
|
|
3208
|
+
className: cx("cs-image", state === "loading" && "is-loading", canPreview && "is-zoomable", className),
|
|
2783
3209
|
style: ratio ? { aspectRatio: ratio } : void 0,
|
|
2784
|
-
onClick:
|
|
2785
|
-
|
|
2786
|
-
|
|
3210
|
+
onClick: canPreview ? openPreview : void 0,
|
|
3211
|
+
onKeyDown: canPreview ? onPreviewKey : void 0,
|
|
3212
|
+
role: canPreview ? "button" : void 0,
|
|
3213
|
+
tabIndex: canPreview ? 0 : void 0,
|
|
3214
|
+
"aria-label": canPreview ? t("preview") + (alt ? ": " + alt : "") : void 0
|
|
2787
3215
|
},
|
|
2788
3216
|
body
|
|
2789
3217
|
), zoom ? /* @__PURE__ */ React.createElement("span", { className: "cs-image__zoom", role: "dialog", "aria-label": alt || t("preview"), onClick: () => setZoom(false) }, /* @__PURE__ */ React.createElement("img", { src, alt }), /* @__PURE__ */ React.createElement("button", { type: "button", "aria-label": t("close"), onClick: () => setZoom(false) }, /* @__PURE__ */ React.createElement(Icon, { name: "close", size: "sm", style: { verticalAlign: "middle" } }))) : null);
|
|
@@ -2791,133 +3219,165 @@ function Image({ src, alt = "", ratio, preview = false, fallback, lang, classNam
|
|
|
2791
3219
|
Object.assign(__ds_scope, { Image });
|
|
2792
3220
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Image.jsx", error: String((e && e.message) || e) }); }
|
|
2793
3221
|
|
|
2794
|
-
// components/
|
|
3222
|
+
// components/datatable/DataGrid.jsx
|
|
2795
3223
|
try { (() => {
|
|
2796
3224
|
const { makeT, useLang } = __ds_scope;
|
|
2797
3225
|
const { Icon } = __ds_scope;
|
|
2798
3226
|
const { cx } = __ds_scope;
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
"
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
"iframe",
|
|
2810
|
-
'[contenteditable]:not([contenteditable="false"])',
|
|
2811
|
-
'[tabindex]:not([tabindex="-1"])'
|
|
2812
|
-
].join(",");
|
|
2813
|
-
function Dialog({
|
|
2814
|
-
open,
|
|
2815
|
-
title,
|
|
2816
|
-
children,
|
|
2817
|
-
actions,
|
|
2818
|
-
onClose,
|
|
2819
|
-
className,
|
|
2820
|
-
closeLabel,
|
|
3227
|
+
function DataGrid({
|
|
3228
|
+
columns = [],
|
|
3229
|
+
rows = [],
|
|
3230
|
+
rowKey = "id",
|
|
3231
|
+
selectable = false,
|
|
3232
|
+
selected = [],
|
|
3233
|
+
onSelect,
|
|
3234
|
+
height = 280,
|
|
3235
|
+
caption,
|
|
3236
|
+
empty,
|
|
2821
3237
|
lang,
|
|
2822
|
-
|
|
3238
|
+
className,
|
|
3239
|
+
filterText,
|
|
3240
|
+
filterKeys,
|
|
3241
|
+
/** When true (or rows exceed virtualThreshold), only paint a window of rows. */
|
|
3242
|
+
virtual = false,
|
|
3243
|
+
/** Row threshold that auto-enables virtualization. Default 80. */
|
|
3244
|
+
virtualThreshold = 80,
|
|
3245
|
+
/** Approximate row height for windowing. Default 36. */
|
|
3246
|
+
rowHeight = 36,
|
|
3247
|
+
/** localStorage key — when set, column key order is persisted across reloads. */
|
|
3248
|
+
persistKey
|
|
2823
3249
|
}) {
|
|
2824
|
-
const
|
|
2825
|
-
const
|
|
2826
|
-
const
|
|
3250
|
+
const [sort, setSort] = React.useState(null);
|
|
3251
|
+
const [scrollTop, setScrollTop] = React.useState(0);
|
|
3252
|
+
const [colOrder, setColOrder] = React.useState(() => {
|
|
3253
|
+
if (!persistKey) return null;
|
|
3254
|
+
try {
|
|
3255
|
+
const raw = localStorage.getItem("cs:datagrid:cols:" + persistKey);
|
|
3256
|
+
const arr = raw ? JSON.parse(raw) : null;
|
|
3257
|
+
return Array.isArray(arr) ? arr : null;
|
|
3258
|
+
} catch (e) {
|
|
3259
|
+
return null;
|
|
3260
|
+
}
|
|
3261
|
+
});
|
|
2827
3262
|
const [ref, L] = useLang(lang);
|
|
2828
|
-
const
|
|
2829
|
-
const
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
const
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
const
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
const
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
{
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
"
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
3263
|
+
const t = makeT("DataGrid", L);
|
|
3264
|
+
const orderedColumns = React.useMemo(() => {
|
|
3265
|
+
if (!colOrder || !colOrder.length) return columns;
|
|
3266
|
+
const map = new Map(columns.map((c) => [c.key, c]));
|
|
3267
|
+
const out = [];
|
|
3268
|
+
for (const k of colOrder) if (map.has(k)) {
|
|
3269
|
+
out.push(map.get(k));
|
|
3270
|
+
map.delete(k);
|
|
3271
|
+
}
|
|
3272
|
+
for (const c of map.values()) out.push(c);
|
|
3273
|
+
return out;
|
|
3274
|
+
}, [columns, colOrder]);
|
|
3275
|
+
const filtered = React.useMemo(() => {
|
|
3276
|
+
const q = (filterText == null ? "" : String(filterText)).trim().toLowerCase();
|
|
3277
|
+
if (!q) return rows;
|
|
3278
|
+
const keys = filterKeys && filterKeys.length ? filterKeys : orderedColumns.map((c) => c.key);
|
|
3279
|
+
return rows.filter((r) => keys.some((k) => String(r[k] == null ? "" : r[k]).toLowerCase().includes(q)));
|
|
3280
|
+
}, [rows, filterText, filterKeys, orderedColumns]);
|
|
3281
|
+
const sorted = React.useMemo(() => {
|
|
3282
|
+
if (!sort) return filtered;
|
|
3283
|
+
const col = orderedColumns.find((c) => c.key === sort.key);
|
|
3284
|
+
const val = (r) => col && col.sortValue ? col.sortValue(r) : r[sort.key];
|
|
3285
|
+
return [...filtered].sort((a, b) => {
|
|
3286
|
+
const x = val(a), y = val(b);
|
|
3287
|
+
return (x > y ? 1 : x < y ? -1 : 0) * (sort.dir === "asc" ? 1 : -1);
|
|
3288
|
+
});
|
|
3289
|
+
}, [filtered, sort, orderedColumns]);
|
|
3290
|
+
const useVirtual = virtual || sorted.length >= virtualThreshold;
|
|
3291
|
+
const bodyH = Math.max(0, height - 40);
|
|
3292
|
+
const start = useVirtual ? Math.max(0, Math.floor(scrollTop / rowHeight) - 4) : 0;
|
|
3293
|
+
const visibleCount = useVirtual ? Math.ceil(bodyH / rowHeight) + 8 : sorted.length;
|
|
3294
|
+
const end = useVirtual ? Math.min(sorted.length, start + visibleCount) : sorted.length;
|
|
3295
|
+
const slice = sorted.slice(start, end);
|
|
3296
|
+
const padTop = useVirtual ? start * rowHeight : 0;
|
|
3297
|
+
const padBottom = useVirtual ? Math.max(0, (sorted.length - end) * rowHeight) : 0;
|
|
3298
|
+
const allSel = selectable && filtered.length && filtered.every((r) => selected.includes(r[rowKey]));
|
|
3299
|
+
const toggleAll = () => onSelect && onSelect(allSel ? [] : filtered.map((r) => r[rowKey]));
|
|
3300
|
+
const toggle = (k) => onSelect && onSelect(selected.includes(k) ? selected.filter((x) => x !== k) : [...selected, k]);
|
|
3301
|
+
const pinColumn = (key) => {
|
|
3302
|
+
if (!persistKey) return;
|
|
3303
|
+
const keys = orderedColumns.map((c) => c.key);
|
|
3304
|
+
const next = [key, ...keys.filter((k) => k !== key)];
|
|
3305
|
+
setColOrder(next);
|
|
3306
|
+
try {
|
|
3307
|
+
localStorage.setItem("cs:datagrid:cols:" + persistKey, JSON.stringify(next));
|
|
3308
|
+
} catch (e) {
|
|
3309
|
+
}
|
|
3310
|
+
};
|
|
3311
|
+
return /* @__PURE__ */ React.createElement(
|
|
3312
|
+
"div",
|
|
3313
|
+
{
|
|
3314
|
+
ref,
|
|
3315
|
+
className: cx("cs-datagrid", useVirtual && "cs-datagrid--virtual", className),
|
|
3316
|
+
style: { maxBlockSize: height, overflow: "auto" },
|
|
3317
|
+
onScroll: useVirtual ? (e) => setScrollTop(e.currentTarget.scrollTop) : void 0,
|
|
3318
|
+
"data-virtual": useVirtual ? "true" : "false",
|
|
3319
|
+
"data-row-count": sorted.length
|
|
3320
|
+
},
|
|
3321
|
+
/* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, selectable ? /* @__PURE__ */ React.createElement("th", { scope: "col", className: "cs-datagrid__selcol" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", "aria-label": t("selectAll"), checked: !!allSel, onChange: toggleAll })) : null, orderedColumns.map((c) => /* @__PURE__ */ React.createElement(
|
|
3322
|
+
"th",
|
|
3323
|
+
{
|
|
3324
|
+
key: c.key,
|
|
3325
|
+
scope: "col",
|
|
3326
|
+
className: c.pinned ? "cs-datagrid__pinned" : void 0,
|
|
3327
|
+
style: c.pinned ? { position: "sticky", insetInlineStart: 0, zIndex: 1, background: "var(--cs-color-surface-panel)" } : void 0,
|
|
3328
|
+
"aria-sort": sort && sort.key === c.key ? sort.dir === "asc" ? "ascending" : "descending" : void 0
|
|
3329
|
+
},
|
|
3330
|
+
c.sortable ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-datagrid__sort", onClick: () => setSort((s) => !s || s.key !== c.key ? { key: c.key, dir: "asc" } : s.dir === "asc" ? { key: c.key, dir: "desc" } : null) }, c.header, /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, sort && sort.key === c.key ? sort.dir === "asc" ? " ▲" : " ▼" : " ↕")) : c.header,
|
|
3331
|
+
persistKey ? /* @__PURE__ */ React.createElement(
|
|
3332
|
+
"button",
|
|
3333
|
+
{
|
|
3334
|
+
type: "button",
|
|
3335
|
+
className: "cs-datagrid__pin",
|
|
3336
|
+
title: t("pin"),
|
|
3337
|
+
onClick: () => pinColumn(c.key),
|
|
3338
|
+
"aria-label": t("pinNamed").replace("{name}", String(c.header != null ? c.header : c.key))
|
|
3339
|
+
},
|
|
3340
|
+
/* @__PURE__ */ React.createElement(Icon, { name: "pin", size: "sm" })
|
|
3341
|
+
) : null
|
|
3342
|
+
)))), /* @__PURE__ */ React.createElement("tbody", null, sorted.length === 0 ? /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), className: "cs-table__empty" }, empty != null ? empty : t("empty"))) : /* @__PURE__ */ React.createElement(React.Fragment, null, padTop > 0 ? /* @__PURE__ */ React.createElement("tr", { "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), style: { height: padTop, padding: 0, border: 0 } })) : null, slice.map((r, i) => {
|
|
3343
|
+
const k = r[rowKey] != null ? r[rowKey] : start + i;
|
|
3344
|
+
return /* @__PURE__ */ React.createElement("tr", { key: k, className: selected.includes(k) ? "is-selected" : void 0, style: useVirtual ? { height: rowHeight } : void 0 }, selectable ? /* @__PURE__ */ React.createElement("td", { className: "cs-datagrid__selcol" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", "aria-label": t("selectRow"), checked: selected.includes(k), onChange: () => toggle(k) })) : null, orderedColumns.map((c) => /* @__PURE__ */ React.createElement("td", { key: c.key, className: c.pinned ? "cs-datagrid__pinned" : void 0, style: c.pinned ? { position: "sticky", insetInlineStart: 0, background: "var(--cs-color-surface-panel)" } : void 0 }, c.render ? c.render(r) : r[c.key])));
|
|
3345
|
+
}), padBottom > 0 ? /* @__PURE__ */ React.createElement("tr", { "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), style: { height: padBottom, padding: 0, border: 0 } })) : null)))
|
|
3346
|
+
);
|
|
3347
|
+
}
|
|
3348
|
+
Object.assign(__ds_scope, { DataGrid });
|
|
3349
|
+
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/datatable/DataGrid.jsx", error: String((e && e.message) || e) }); }
|
|
3350
|
+
|
|
3351
|
+
// components/logo/logo-data.js
|
|
3352
|
+
try { (() => {
|
|
3353
|
+
const CS_LOGO_VIEWBOX = "0 0 1007 1007";
|
|
3354
|
+
const CS_LOGO_MARK_INNER = `<path fill="#45210E" opacity="1.000000" stroke="none"
|
|
3355
|
+
d="
|
|
3356
|
+
M560.000000,1008.000000
|
|
3357
|
+
C373.333344,1008.000000 187.166672,1008.000000 1.000000,1008.000000
|
|
3358
|
+
C1.000000,672.333313 1.000000,336.666656 1.000000,1.000000
|
|
3359
|
+
C336.666656,1.000000 672.333313,1.000000 1008.000000,1.000000
|
|
3360
|
+
C1008.000000,336.666656 1008.000000,672.333313 1008.000000,1008.000000
|
|
3361
|
+
C858.833313,1008.000000 709.666687,1008.000000 560.000000,1008.000000
|
|
3362
|
+
M440.743774,105.242386
|
|
3363
|
+
C438.572540,107.309639 436.344940,109.321198 434.238464,111.452431
|
|
3364
|
+
C396.510132,149.623566 372.276398,194.939011 363.356293,247.940109
|
|
3365
|
+
C360.996429,261.961884 358.946777,276.673065 360.603027,290.610352
|
|
3366
|
+
C365.030243,327.864777 394.910004,357.905762 431.834900,364.830811
|
|
3367
|
+
C436.723083,365.747589 441.737213,365.993011 447.763702,366.669525
|
|
3368
|
+
C446.254150,363.858124 445.507843,362.409912 444.709564,360.990906
|
|
3369
|
+
C439.517578,351.761383 436.885193,341.740936 437.455444,331.277863
|
|
3370
|
+
C439.589569,292.120056 458.815063,264.154144 494.094208,247.402267
|
|
3371
|
+
C503.544983,242.914673 513.742554,238.863556 524.373901,240.455948
|
|
3372
|
+
C548.543213,244.076111 568.703491,255.555466 584.000793,274.713074
|
|
3373
|
+
C599.847656,294.559021 607.550598,317.209198 603.764282,342.677429
|
|
3374
|
+
C602.575073,350.677063 598.728027,358.281586 595.936157,366.544159
|
|
3375
|
+
C600.137085,366.074554 604.740540,365.758667 609.275574,365.021576
|
|
3376
|
+
C646.763123,358.928711 678.394348,326.946808 681.791199,289.350922
|
|
3377
|
+
C683.086792,275.012085 681.404419,259.972870 678.550415,245.760666
|
|
3378
|
+
C660.577881,156.262833 608.759338,93.666801 525.888672,56.228947
|
|
3379
|
+
C522.563171,54.726585 519.736023,54.769550 516.419739,56.200459
|
|
3380
|
+
C488.640594,68.186523 463.511292,84.231010 440.743774,105.242386
|
|
2921
3381
|
M286.676636,492.678345
|
|
2922
3382
|
C287.329773,493.793732 287.835724,495.033447 288.657562,496.006378
|
|
2923
3383
|
C309.770172,521.000305 334.755707,541.223022 364.045837,555.811401
|
|
@@ -3193,37 +3653,6 @@ function Breadcrumb({ items = [], lang, className, ...props }) {
|
|
|
3193
3653
|
Object.assign(__ds_scope, { Breadcrumb });
|
|
3194
3654
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/Breadcrumb.jsx", error: String((e && e.message) || e) }); }
|
|
3195
3655
|
|
|
3196
|
-
// components/navigation/CommandPalette.jsx
|
|
3197
|
-
try { (() => {
|
|
3198
|
-
const { makeT, useLang } = __ds_scope;
|
|
3199
|
-
const { cx } = __ds_scope;
|
|
3200
|
-
function CommandPalette({ open, onClose, placeholder, groups = [], lang, className }) {
|
|
3201
|
-
const [q, setQ] = React.useState("");
|
|
3202
|
-
React.useEffect(() => {
|
|
3203
|
-
if (!open) return;
|
|
3204
|
-
const onKey = (e) => {
|
|
3205
|
-
if (e.key === "Escape") onClose && onClose();
|
|
3206
|
-
};
|
|
3207
|
-
document.addEventListener("keydown", onKey);
|
|
3208
|
-
return () => document.removeEventListener("keydown", onKey);
|
|
3209
|
-
}, [open]);
|
|
3210
|
-
const [ref, L] = useLang(lang);
|
|
3211
|
-
const t = makeT("CommandPalette", L);
|
|
3212
|
-
const ph = placeholder != null ? placeholder : t("placeholder");
|
|
3213
|
-
if (!open) return null;
|
|
3214
|
-
const needle = q.trim().toLowerCase();
|
|
3215
|
-
return /* @__PURE__ */ React.createElement("div", { ref, className: "cs-cmdk-scrim", onClick: onClose }, /* @__PURE__ */ React.createElement("div", { className: cx("cs-cmdk", className), role: "dialog", "aria-modal": "true", "aria-label": t("aria"), onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__search" }, /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("circle", { cx: "11", cy: "11", r: "7" }), /* @__PURE__ */ React.createElement("path", { d: "M21 21l-4.3-4.3" })), /* @__PURE__ */ React.createElement("input", { autoFocus: true, value: q, onChange: (e) => setQ(e.target.value), placeholder: ph }), /* @__PURE__ */ React.createElement("span", { className: "cs-kbd" }, t("esc"))), /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__list" }, groups.map((g, gi) => {
|
|
3216
|
-
const items = (g.items || []).filter((it) => !needle || String(it.label).toLowerCase().includes(needle));
|
|
3217
|
-
if (!items.length) return null;
|
|
3218
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, { key: gi }, g.label ? /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__label" }, g.label) : null, items.map((it, ii) => /* @__PURE__ */ React.createElement("button", { key: ii, type: "button", className: "cs-cmdk__item", onClick: () => {
|
|
3219
|
-
it.onSelect && it.onSelect();
|
|
3220
|
-
onClose && onClose();
|
|
3221
|
-
} }, it.icon ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, it.icon) : null, /* @__PURE__ */ React.createElement("span", null, it.label), it.shortcut ? /* @__PURE__ */ React.createElement("span", { className: "cs-kbd" }, it.shortcut) : null)));
|
|
3222
|
-
}))));
|
|
3223
|
-
}
|
|
3224
|
-
Object.assign(__ds_scope, { CommandPalette });
|
|
3225
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/CommandPalette.jsx", error: String((e && e.message) || e) }); }
|
|
3226
|
-
|
|
3227
3656
|
// components/navigation/Dock.jsx
|
|
3228
3657
|
try { (() => {
|
|
3229
3658
|
const { cx } = __ds_scope;
|
|
@@ -3313,21 +3742,51 @@ Object.assign(__ds_scope, { Link });
|
|
|
3313
3742
|
|
|
3314
3743
|
// components/navigation/Menu.jsx
|
|
3315
3744
|
try { (() => {
|
|
3745
|
+
const { makeT, useLang } = __ds_scope;
|
|
3316
3746
|
const { cx } = __ds_scope;
|
|
3317
|
-
function Menu({ trigger, children, align = "start", open: controlledOpen, onOpenChange, className }) {
|
|
3747
|
+
function Menu({ trigger, children, align = "start", open: controlledOpen, onOpenChange, lang, className }) {
|
|
3318
3748
|
const [uOpen, setUOpen] = React.useState(false);
|
|
3319
3749
|
const open = controlledOpen != null ? controlledOpen : uOpen;
|
|
3320
3750
|
const set = (v) => {
|
|
3321
3751
|
onOpenChange ? onOpenChange(v) : setUOpen(v);
|
|
3322
3752
|
};
|
|
3323
|
-
const ref =
|
|
3753
|
+
const [ref, L] = useLang(lang);
|
|
3754
|
+
const t = makeT("Menu", L);
|
|
3755
|
+
const triggerRef = React.useRef(null);
|
|
3756
|
+
const listRef = React.useRef(null);
|
|
3324
3757
|
React.useEffect(() => {
|
|
3325
3758
|
if (!open) return;
|
|
3759
|
+
const items = () => listRef.current ? [...listRef.current.querySelectorAll('[role="menuitem"]:not([disabled])')] : [];
|
|
3760
|
+
const first = items()[0];
|
|
3761
|
+
if (first && first.focus) first.focus();
|
|
3326
3762
|
const onDoc = (e) => {
|
|
3327
3763
|
if (ref.current && !ref.current.contains(e.target)) set(false);
|
|
3328
3764
|
};
|
|
3329
3765
|
const onKey = (e) => {
|
|
3330
|
-
|
|
3766
|
+
const opts = items();
|
|
3767
|
+
if (e.key === "Escape") {
|
|
3768
|
+
e.preventDefault();
|
|
3769
|
+
set(false);
|
|
3770
|
+
const el = triggerRef.current;
|
|
3771
|
+
if (el && el.focus) el.focus();
|
|
3772
|
+
return;
|
|
3773
|
+
}
|
|
3774
|
+
if (!opts.length) return;
|
|
3775
|
+
const active = document.activeElement;
|
|
3776
|
+
const idx = opts.indexOf(active);
|
|
3777
|
+
if (e.key === "ArrowDown") {
|
|
3778
|
+
e.preventDefault();
|
|
3779
|
+
(opts[idx < 0 ? 0 : Math.min(opts.length - 1, idx + 1)] || opts[0]).focus();
|
|
3780
|
+
} else if (e.key === "ArrowUp") {
|
|
3781
|
+
e.preventDefault();
|
|
3782
|
+
(opts[idx < 0 ? opts.length - 1 : Math.max(0, idx - 1)] || opts[0]).focus();
|
|
3783
|
+
} else if (e.key === "Home") {
|
|
3784
|
+
e.preventDefault();
|
|
3785
|
+
opts[0].focus();
|
|
3786
|
+
} else if (e.key === "End") {
|
|
3787
|
+
e.preventDefault();
|
|
3788
|
+
opts[opts.length - 1].focus();
|
|
3789
|
+
}
|
|
3331
3790
|
};
|
|
3332
3791
|
document.addEventListener("mousedown", onDoc);
|
|
3333
3792
|
document.addEventListener("keydown", onKey);
|
|
@@ -3338,14 +3797,29 @@ function Menu({ trigger, children, align = "start", open: controlledOpen, onOpen
|
|
|
3338
3797
|
}, [open]);
|
|
3339
3798
|
const toggle = () => set(!open);
|
|
3340
3799
|
const triggerNode = React.isValidElement(trigger) ? React.cloneElement(trigger, {
|
|
3800
|
+
ref: (node) => {
|
|
3801
|
+
triggerRef.current = node;
|
|
3802
|
+
const r = trigger.ref;
|
|
3803
|
+
if (typeof r === "function") r(node);
|
|
3804
|
+
else if (r && typeof r === "object") r.current = node;
|
|
3805
|
+
},
|
|
3341
3806
|
"aria-haspopup": "menu",
|
|
3342
3807
|
"aria-expanded": open,
|
|
3343
3808
|
onClick: (e) => {
|
|
3344
3809
|
if (typeof trigger.props.onClick === "function") trigger.props.onClick(e);
|
|
3345
3810
|
if (!e.defaultPrevented) toggle();
|
|
3346
3811
|
}
|
|
3347
|
-
}) : /* @__PURE__ */ React.createElement("button", { type: "button", "aria-haspopup": "menu", "aria-expanded": open, onClick: toggle }, trigger);
|
|
3348
|
-
return /* @__PURE__ */ React.createElement("div", { className: cx("cs-menu", className), ref }, triggerNode, open ? /* @__PURE__ */ React.createElement(
|
|
3812
|
+
}) : /* @__PURE__ */ React.createElement("button", { type: "button", ref: triggerRef, "aria-haspopup": "menu", "aria-expanded": open, onClick: toggle }, trigger);
|
|
3813
|
+
return /* @__PURE__ */ React.createElement("div", { className: cx("cs-menu", className), ref }, triggerNode, open ? /* @__PURE__ */ React.createElement(
|
|
3814
|
+
"div",
|
|
3815
|
+
{
|
|
3816
|
+
ref: listRef,
|
|
3817
|
+
className: cx("cs-menu__list", align === "end" && "cs-menu__list--end"),
|
|
3818
|
+
role: "menu",
|
|
3819
|
+
"aria-label": t("menu")
|
|
3820
|
+
},
|
|
3821
|
+
children
|
|
3822
|
+
) : null);
|
|
3349
3823
|
}
|
|
3350
3824
|
function MenuItem({ danger = false, icon, children, className, ...props }) {
|
|
3351
3825
|
return /* @__PURE__ */ React.createElement("button", { type: "button", role: "menuitem", className: cx("cs-menu__item", danger && "cs-menu__item--danger", className), ...props }, icon ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, icon) : null, children);
|
|
@@ -3506,11 +3980,14 @@ Object.assign(__ds_scope, { Sidebar, NavItem });
|
|
|
3506
3980
|
|
|
3507
3981
|
// components/navigation/Steps.jsx
|
|
3508
3982
|
try { (() => {
|
|
3983
|
+
const { makeT, useLang } = __ds_scope;
|
|
3509
3984
|
const { cx } = __ds_scope;
|
|
3510
|
-
function Steps({ steps = [], current = 0, className }) {
|
|
3511
|
-
|
|
3985
|
+
function Steps({ steps = [], current = 0, lang, className }) {
|
|
3986
|
+
const [ref, L] = useLang(lang);
|
|
3987
|
+
const t = makeT("Steps", L);
|
|
3988
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-steps", className), role: "list", "aria-label": t("label") }, steps.map((s, i) => {
|
|
3512
3989
|
const state = i < current ? "done" : i === current ? "active" : "todo";
|
|
3513
|
-
return /* @__PURE__ */ React.createElement("div", { key: i, className: cx("cs-step", `cs-step--${state}`) }, /* @__PURE__ */ React.createElement("span", { className: "cs-step__marker" }, state === "done" ? "✓" : s.n || i + 1), /* @__PURE__ */ React.createElement("span", { className: "cs-step__title" }, s.title), s.body ? /* @__PURE__ */ React.createElement("span", { className: "cs-step__body" }, s.body) : null);
|
|
3990
|
+
return /* @__PURE__ */ React.createElement("div", { key: i, className: cx("cs-step", `cs-step--${state}`), role: "listitem" }, /* @__PURE__ */ React.createElement("span", { className: "cs-step__marker" }, state === "done" ? "✓" : s.n || i + 1), /* @__PURE__ */ React.createElement("span", { className: "cs-step__title" }, s.title), s.body ? /* @__PURE__ */ React.createElement("span", { className: "cs-step__body" }, s.body) : null);
|
|
3514
3991
|
}));
|
|
3515
3992
|
}
|
|
3516
3993
|
Object.assign(__ds_scope, { Steps });
|
|
@@ -3518,10 +3995,13 @@ Object.assign(__ds_scope, { Steps });
|
|
|
3518
3995
|
|
|
3519
3996
|
// components/navigation/Tabs.jsx
|
|
3520
3997
|
try { (() => {
|
|
3998
|
+
const { makeT, useLang } = __ds_scope;
|
|
3521
3999
|
const { cx } = __ds_scope;
|
|
3522
|
-
function Tabs({ tabs = [], value, onChange, className, ...props }) {
|
|
4000
|
+
function Tabs({ tabs = [], value, onChange, lang, className, "aria-label": ariaLabel, ...props }) {
|
|
4001
|
+
const [ref, L] = useLang(lang);
|
|
4002
|
+
const t = makeT("Tabs", L);
|
|
3523
4003
|
const refs = React.useRef([]);
|
|
3524
|
-
const idx = Math.max(0, tabs.findIndex((
|
|
4004
|
+
const idx = Math.max(0, tabs.findIndex((tab) => tab.value === value));
|
|
3525
4005
|
const key = (e, i) => {
|
|
3526
4006
|
let n = null;
|
|
3527
4007
|
if (e.key === "ArrowRight") n = (i + 1) % tabs.length;
|
|
@@ -3534,21 +4014,21 @@ function Tabs({ tabs = [], value, onChange, className, ...props }) {
|
|
|
3534
4014
|
const b = refs.current[n];
|
|
3535
4015
|
if (b) b.focus();
|
|
3536
4016
|
};
|
|
3537
|
-
return /* @__PURE__ */ React.createElement("div", { role: "tablist", className: cx("cs-tabs", className), ...props }, tabs.map((
|
|
4017
|
+
return /* @__PURE__ */ React.createElement("div", { ref, role: "tablist", "aria-label": ariaLabel ?? t("list"), className: cx("cs-tabs", className), ...props }, tabs.map((t2, i) => /* @__PURE__ */ React.createElement(
|
|
3538
4018
|
"button",
|
|
3539
4019
|
{
|
|
3540
|
-
key:
|
|
4020
|
+
key: t2.value,
|
|
3541
4021
|
type: "button",
|
|
3542
4022
|
role: "tab",
|
|
3543
|
-
"aria-selected": value ===
|
|
4023
|
+
"aria-selected": value === t2.value,
|
|
3544
4024
|
tabIndex: i === idx ? 0 : -1,
|
|
3545
4025
|
ref: (el) => refs.current[i] = el,
|
|
3546
4026
|
onKeyDown: (e) => key(e, i),
|
|
3547
4027
|
className: "cs-tab",
|
|
3548
|
-
onClick: () => onChange && onChange(
|
|
4028
|
+
onClick: () => onChange && onChange(t2.value)
|
|
3549
4029
|
},
|
|
3550
|
-
|
|
3551
|
-
|
|
4030
|
+
t2.label,
|
|
4031
|
+
t2.count != null ? /* @__PURE__ */ React.createElement("span", { className: "cs-tab__count" }, t2.count) : null
|
|
3552
4032
|
)));
|
|
3553
4033
|
}
|
|
3554
4034
|
function Tab({ selected = false, count, children, className, ...props }) {
|
|
@@ -3563,11 +4043,21 @@ const { makeT, useLang } = __ds_scope;
|
|
|
3563
4043
|
const { cx } = __ds_scope;
|
|
3564
4044
|
function Toolbar({ items = [], overflowAfter, label, lang, className }) {
|
|
3565
4045
|
const [open, setOpen] = React.useState(false);
|
|
4046
|
+
const [focusIdx, setFocusIdx] = React.useState(0);
|
|
3566
4047
|
const wrap = React.useRef(null);
|
|
4048
|
+
const btnRefs = React.useRef([]);
|
|
4049
|
+
const menuRefs = React.useRef([]);
|
|
3567
4050
|
const [ref, L] = useLang(lang);
|
|
3568
4051
|
const t = makeT("Toolbar", L);
|
|
3569
4052
|
const cut = overflowAfter != null ? overflowAfter : items.length;
|
|
3570
|
-
const head = items.slice(0, cut)
|
|
4053
|
+
const head = items.slice(0, cut);
|
|
4054
|
+
const tail = items.slice(cut).filter((x) => x !== "-");
|
|
4055
|
+
const hasMore = tail.length > 0;
|
|
4056
|
+
const controlMeta = [];
|
|
4057
|
+
head.forEach((it, i) => {
|
|
4058
|
+
if (it !== "-") controlMeta.push({ kind: "head", headIndex: i });
|
|
4059
|
+
});
|
|
4060
|
+
if (hasMore) controlMeta.push({ kind: "more" });
|
|
3571
4061
|
React.useEffect(() => {
|
|
3572
4062
|
if (!open) return;
|
|
3573
4063
|
const d = (e) => {
|
|
@@ -3576,22 +4066,210 @@ function Toolbar({ items = [], overflowAfter, label, lang, className }) {
|
|
|
3576
4066
|
document.addEventListener("mousedown", d);
|
|
3577
4067
|
return () => document.removeEventListener("mousedown", d);
|
|
3578
4068
|
}, [open]);
|
|
4069
|
+
React.useEffect(() => {
|
|
4070
|
+
if (focusIdx >= controlMeta.length) setFocusIdx(Math.max(0, controlMeta.length - 1));
|
|
4071
|
+
}, [focusIdx, controlMeta.length]);
|
|
4072
|
+
const focusControl = (i) => {
|
|
4073
|
+
const next = Math.max(0, Math.min(controlMeta.length - 1, i));
|
|
4074
|
+
setFocusIdx(next);
|
|
4075
|
+
requestAnimationFrame(() => {
|
|
4076
|
+
const el = btnRefs.current[next];
|
|
4077
|
+
if (el) el.focus();
|
|
4078
|
+
});
|
|
4079
|
+
};
|
|
4080
|
+
const onToolbarKeyDown = (e, i) => {
|
|
4081
|
+
if (e.nativeEvent.isComposing || e.keyCode === 229) return;
|
|
4082
|
+
if (e.key === "ArrowRight") {
|
|
4083
|
+
e.preventDefault();
|
|
4084
|
+
focusControl(i + 1);
|
|
4085
|
+
} else if (e.key === "ArrowLeft") {
|
|
4086
|
+
e.preventDefault();
|
|
4087
|
+
focusControl(i - 1);
|
|
4088
|
+
} else if (e.key === "Home") {
|
|
4089
|
+
e.preventDefault();
|
|
4090
|
+
focusControl(0);
|
|
4091
|
+
} else if (e.key === "End") {
|
|
4092
|
+
e.preventDefault();
|
|
4093
|
+
focusControl(controlMeta.length - 1);
|
|
4094
|
+
} else if (e.key === "Escape" && open) {
|
|
4095
|
+
e.preventDefault();
|
|
4096
|
+
setOpen(false);
|
|
4097
|
+
}
|
|
4098
|
+
};
|
|
4099
|
+
const onMenuKeyDown = (e, i) => {
|
|
4100
|
+
if (e.nativeEvent.isComposing || e.keyCode === 229) return;
|
|
4101
|
+
if (e.key === "ArrowDown") {
|
|
4102
|
+
e.preventDefault();
|
|
4103
|
+
const n = Math.min(tail.length - 1, i + 1);
|
|
4104
|
+
const el = menuRefs.current[n];
|
|
4105
|
+
if (el) el.focus();
|
|
4106
|
+
} else if (e.key === "ArrowUp") {
|
|
4107
|
+
e.preventDefault();
|
|
4108
|
+
const n = Math.max(0, i - 1);
|
|
4109
|
+
const el = menuRefs.current[n];
|
|
4110
|
+
if (el) el.focus();
|
|
4111
|
+
} else if (e.key === "Escape") {
|
|
4112
|
+
e.preventDefault();
|
|
4113
|
+
setOpen(false);
|
|
4114
|
+
const moreIdx = controlMeta.findIndex((c) => c.kind === "more");
|
|
4115
|
+
if (moreIdx >= 0) focusControl(moreIdx);
|
|
4116
|
+
} else if (e.key === "Home") {
|
|
4117
|
+
e.preventDefault();
|
|
4118
|
+
const el = menuRefs.current[0];
|
|
4119
|
+
if (el) el.focus();
|
|
4120
|
+
} else if (e.key === "End") {
|
|
4121
|
+
e.preventDefault();
|
|
4122
|
+
const el = menuRefs.current[tail.length - 1];
|
|
4123
|
+
if (el) el.focus();
|
|
4124
|
+
}
|
|
4125
|
+
};
|
|
4126
|
+
let controlI = 0;
|
|
3579
4127
|
return /* @__PURE__ */ React.createElement("div", { ref: (el) => {
|
|
3580
4128
|
wrap.current = el;
|
|
3581
4129
|
ref.current = el;
|
|
3582
|
-
}, className: cx("cs-toolbar", className), role: "toolbar", "aria-label": label
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
4130
|
+
}, className: cx("cs-toolbar", className), role: "toolbar", "aria-label": label, "aria-orientation": "horizontal" }, head.map((it, i) => {
|
|
4131
|
+
if (it === "-") return /* @__PURE__ */ React.createElement("span", { key: "sep-" + i, className: "cs-toolbar__sep", "aria-hidden": "true" });
|
|
4132
|
+
const ci = controlI++;
|
|
4133
|
+
return /* @__PURE__ */ React.createElement(
|
|
4134
|
+
"button",
|
|
4135
|
+
{
|
|
4136
|
+
key: "btn-" + i,
|
|
4137
|
+
type: "button",
|
|
4138
|
+
className: "cs-toolbar__btn",
|
|
4139
|
+
tabIndex: ci === focusIdx ? 0 : -1,
|
|
4140
|
+
ref: (el) => {
|
|
4141
|
+
btnRefs.current[ci] = el;
|
|
4142
|
+
},
|
|
4143
|
+
onFocus: () => setFocusIdx(ci),
|
|
4144
|
+
onKeyDown: (e) => onToolbarKeyDown(e, ci),
|
|
4145
|
+
onClick: () => it.onSelect && it.onSelect()
|
|
4146
|
+
},
|
|
4147
|
+
it.icon,
|
|
4148
|
+
it.label ? /* @__PURE__ */ React.createElement("span", null, it.label) : null
|
|
4149
|
+
);
|
|
4150
|
+
}), hasMore ? (() => {
|
|
4151
|
+
const ci = controlI++;
|
|
4152
|
+
return /* @__PURE__ */ React.createElement("span", { className: "cs-toolbar__more" }, /* @__PURE__ */ React.createElement(
|
|
4153
|
+
"button",
|
|
4154
|
+
{
|
|
4155
|
+
type: "button",
|
|
4156
|
+
className: "cs-toolbar__btn",
|
|
4157
|
+
"aria-haspopup": "menu",
|
|
4158
|
+
"aria-expanded": open,
|
|
4159
|
+
"aria-label": t("more"),
|
|
4160
|
+
tabIndex: ci === focusIdx ? 0 : -1,
|
|
4161
|
+
ref: (el) => {
|
|
4162
|
+
btnRefs.current[ci] = el;
|
|
4163
|
+
},
|
|
4164
|
+
onFocus: () => setFocusIdx(ci),
|
|
4165
|
+
onKeyDown: (e) => {
|
|
4166
|
+
onToolbarKeyDown(e, ci);
|
|
4167
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
4168
|
+
if (!open) {
|
|
4169
|
+
e.preventDefault();
|
|
4170
|
+
setOpen(true);
|
|
4171
|
+
requestAnimationFrame(() => {
|
|
4172
|
+
const el = menuRefs.current[0];
|
|
4173
|
+
if (el) el.focus();
|
|
4174
|
+
});
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
},
|
|
4178
|
+
onClick: () => setOpen((o) => !o)
|
|
4179
|
+
},
|
|
4180
|
+
"⋯"
|
|
4181
|
+
), open ? /* @__PURE__ */ React.createElement("span", { className: "cs-menu__list", role: "menu" }, tail.map((it, i) => /* @__PURE__ */ React.createElement(
|
|
4182
|
+
"button",
|
|
4183
|
+
{
|
|
4184
|
+
key: i,
|
|
4185
|
+
type: "button",
|
|
4186
|
+
role: "menuitem",
|
|
4187
|
+
className: "cs-menu__item",
|
|
4188
|
+
ref: (el) => {
|
|
4189
|
+
menuRefs.current[i] = el;
|
|
4190
|
+
},
|
|
4191
|
+
onKeyDown: (e) => onMenuKeyDown(e, i),
|
|
4192
|
+
onClick: () => {
|
|
4193
|
+
setOpen(false);
|
|
4194
|
+
it.onSelect && it.onSelect();
|
|
4195
|
+
}
|
|
4196
|
+
},
|
|
4197
|
+
it.label
|
|
4198
|
+
))) : null);
|
|
4199
|
+
})() : null);
|
|
3586
4200
|
}
|
|
3587
4201
|
Object.assign(__ds_scope, { Toolbar });
|
|
3588
4202
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/Toolbar.jsx", error: String((e && e.message) || e) }); }
|
|
3589
4203
|
|
|
3590
|
-
// components/overlays/
|
|
4204
|
+
// components/overlays/ContextMenu.jsx
|
|
3591
4205
|
try { (() => {
|
|
3592
4206
|
const { makeT, useLang } = __ds_scope;
|
|
3593
4207
|
const { cx } = __ds_scope;
|
|
3594
|
-
|
|
4208
|
+
function ContextMenu({ items = [], children, lang, className }) {
|
|
4209
|
+
const [pos, setPos] = React.useState(null);
|
|
4210
|
+
const [ref, L] = useLang(lang);
|
|
4211
|
+
const t = makeT("ContextMenu", L);
|
|
4212
|
+
React.useEffect(() => {
|
|
4213
|
+
if (!pos) return;
|
|
4214
|
+
const close = () => setPos(null);
|
|
4215
|
+
const k = (e) => {
|
|
4216
|
+
if (e.key === "Escape") setPos(null);
|
|
4217
|
+
};
|
|
4218
|
+
document.addEventListener("click", close);
|
|
4219
|
+
document.addEventListener("keydown", k);
|
|
4220
|
+
return () => {
|
|
4221
|
+
document.removeEventListener("click", close);
|
|
4222
|
+
document.removeEventListener("keydown", k);
|
|
4223
|
+
};
|
|
4224
|
+
}, [pos]);
|
|
4225
|
+
return /* @__PURE__ */ React.createElement(
|
|
4226
|
+
"div",
|
|
4227
|
+
{
|
|
4228
|
+
ref,
|
|
4229
|
+
className: cx("cs-ctxmenu-zone", className),
|
|
4230
|
+
onContextMenu: (e) => {
|
|
4231
|
+
e.preventDefault();
|
|
4232
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
4233
|
+
setPos({ x: e.clientX - r.left, y: e.clientY - r.top });
|
|
4234
|
+
}
|
|
4235
|
+
},
|
|
4236
|
+
children,
|
|
4237
|
+
pos ? /* @__PURE__ */ React.createElement("div", { className: "cs-menu__list", role: "menu", "aria-label": t("menu"), style: { position: "absolute", insetInlineStart: pos.x, insetBlockStart: pos.y } }, items.map((it, i) => it === "-" ? /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-menu__sep" }) : /* @__PURE__ */ React.createElement("button", { key: i, type: "button", role: "menuitem", className: cx("cs-menu__item", it.danger && "cs-menu__item--danger"), onClick: () => {
|
|
4238
|
+
setPos(null);
|
|
4239
|
+
it.onSelect && it.onSelect();
|
|
4240
|
+
} }, it.label))) : null
|
|
4241
|
+
);
|
|
4242
|
+
}
|
|
4243
|
+
Object.assign(__ds_scope, { ContextMenu });
|
|
4244
|
+
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/ContextMenu.jsx", error: String((e && e.message) || e) }); }
|
|
4245
|
+
|
|
4246
|
+
// components/overlays/HoverCard.jsx
|
|
4247
|
+
try { (() => {
|
|
4248
|
+
const { cx } = __ds_scope;
|
|
4249
|
+
function HoverCard({ trigger, children, openDelay = 150, closeDelay = 200, className }) {
|
|
4250
|
+
const [open, setOpen] = React.useState(false);
|
|
4251
|
+
const t1 = React.useRef();
|
|
4252
|
+
const t2 = React.useRef();
|
|
4253
|
+
const show = () => {
|
|
4254
|
+
clearTimeout(t2.current);
|
|
4255
|
+
t1.current = setTimeout(() => setOpen(true), openDelay);
|
|
4256
|
+
};
|
|
4257
|
+
const hide = () => {
|
|
4258
|
+
clearTimeout(t1.current);
|
|
4259
|
+
t2.current = setTimeout(() => setOpen(false), closeDelay);
|
|
4260
|
+
};
|
|
4261
|
+
React.useEffect(() => () => {
|
|
4262
|
+
clearTimeout(t1.current);
|
|
4263
|
+
clearTimeout(t2.current);
|
|
4264
|
+
}, []);
|
|
4265
|
+
return /* @__PURE__ */ React.createElement("span", { className: cx("cs-hovercard", className), onMouseEnter: show, onMouseLeave: hide, onFocus: show, onBlur: hide }, trigger, open ? /* @__PURE__ */ React.createElement("span", { className: "cs-hovercard__panel", role: "dialog" }, children) : null);
|
|
4266
|
+
}
|
|
4267
|
+
Object.assign(__ds_scope, { HoverCard });
|
|
4268
|
+
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/HoverCard.jsx", error: String((e && e.message) || e) }); }
|
|
4269
|
+
|
|
4270
|
+
// components/overlays/OverlayManager.jsx
|
|
4271
|
+
try { (() => {
|
|
4272
|
+
const focusableSelector = [
|
|
3595
4273
|
"a[href]",
|
|
3596
4274
|
"area[href]",
|
|
3597
4275
|
"button:not([disabled])",
|
|
@@ -3605,6 +4283,270 @@ const FOCUSABLE = [
|
|
|
3605
4283
|
'[contenteditable]:not([contenteditable="false"])',
|
|
3606
4284
|
'[tabindex]:not([tabindex="-1"])'
|
|
3607
4285
|
].join(",");
|
|
4286
|
+
function attachFocusTrap(panelEl, { handleEscape = false, onEscape } = {}) {
|
|
4287
|
+
if (!panelEl) return () => {
|
|
4288
|
+
};
|
|
4289
|
+
const focusables = () => [...panelEl.querySelectorAll(focusableSelector)];
|
|
4290
|
+
const k = (e) => {
|
|
4291
|
+
if (handleEscape && e.key === "Escape") {
|
|
4292
|
+
onEscape && onEscape();
|
|
4293
|
+
return;
|
|
4294
|
+
}
|
|
4295
|
+
if (e.key !== "Tab") return;
|
|
4296
|
+
const f = focusables();
|
|
4297
|
+
if (!f.length) {
|
|
4298
|
+
e.preventDefault();
|
|
4299
|
+
panelEl.focus();
|
|
4300
|
+
return;
|
|
4301
|
+
}
|
|
4302
|
+
const a = f[0];
|
|
4303
|
+
const z = f[f.length - 1];
|
|
4304
|
+
const active = document.activeElement;
|
|
4305
|
+
const inside = panelEl.contains(active);
|
|
4306
|
+
if (e.shiftKey && (!inside || active === a)) {
|
|
4307
|
+
e.preventDefault();
|
|
4308
|
+
z.focus();
|
|
4309
|
+
} else if (!e.shiftKey && (!inside || active === z)) {
|
|
4310
|
+
e.preventDefault();
|
|
4311
|
+
a.focus();
|
|
4312
|
+
}
|
|
4313
|
+
};
|
|
4314
|
+
document.addEventListener("keydown", k);
|
|
4315
|
+
return () => document.removeEventListener("keydown", k);
|
|
4316
|
+
}
|
|
4317
|
+
function createOverlayManager() {
|
|
4318
|
+
const layers = [];
|
|
4319
|
+
let prevOverflow = "";
|
|
4320
|
+
let locked = false;
|
|
4321
|
+
let escapeBound = false;
|
|
4322
|
+
const resort = () => {
|
|
4323
|
+
layers.sort((a, b) => {
|
|
4324
|
+
const pa = a.panelEl;
|
|
4325
|
+
const pb = b.panelEl;
|
|
4326
|
+
if (pa && pb && pa !== pb) {
|
|
4327
|
+
if (pa.contains(pb)) return -1;
|
|
4328
|
+
if (pb.contains(pa)) return 1;
|
|
4329
|
+
}
|
|
4330
|
+
return (a.seq || 0) - (b.seq || 0);
|
|
4331
|
+
});
|
|
4332
|
+
};
|
|
4333
|
+
const applyScroll = () => {
|
|
4334
|
+
const need = layers.some((l) => l.kind === "modal" && l.lockScroll !== false);
|
|
4335
|
+
if (need && !locked) {
|
|
4336
|
+
prevOverflow = document.body.style.overflow;
|
|
4337
|
+
document.body.style.overflow = "hidden";
|
|
4338
|
+
locked = true;
|
|
4339
|
+
} else if (!need && locked) {
|
|
4340
|
+
document.body.style.overflow = prevOverflow;
|
|
4341
|
+
locked = false;
|
|
4342
|
+
}
|
|
4343
|
+
};
|
|
4344
|
+
const onDocEscape = (e) => {
|
|
4345
|
+
if (e.key !== "Escape") return;
|
|
4346
|
+
if (!layers.length) return;
|
|
4347
|
+
resort();
|
|
4348
|
+
const top = layers[layers.length - 1];
|
|
4349
|
+
if (!top || typeof top.onEscape !== "function") return;
|
|
4350
|
+
e.preventDefault();
|
|
4351
|
+
e.stopPropagation();
|
|
4352
|
+
top.onEscape();
|
|
4353
|
+
};
|
|
4354
|
+
const ensureEscape = () => {
|
|
4355
|
+
if (escapeBound) return;
|
|
4356
|
+
escapeBound = true;
|
|
4357
|
+
document.addEventListener("keydown", onDocEscape, true);
|
|
4358
|
+
};
|
|
4359
|
+
let seq = 0;
|
|
4360
|
+
return {
|
|
4361
|
+
register(layer) {
|
|
4362
|
+
ensureEscape();
|
|
4363
|
+
const id = layer.id || `cs-ov-${++seq}`;
|
|
4364
|
+
const entry = { ...layer, id, seq: ++seq };
|
|
4365
|
+
layers.push(entry);
|
|
4366
|
+
resort();
|
|
4367
|
+
applyScroll();
|
|
4368
|
+
return () => {
|
|
4369
|
+
resort();
|
|
4370
|
+
const i = layers.findIndex((l) => l.id === id);
|
|
4371
|
+
const wasTop = i === layers.length - 1;
|
|
4372
|
+
if (i >= 0) layers.splice(i, 1);
|
|
4373
|
+
applyScroll();
|
|
4374
|
+
if (wasTop && entry.restoreEl && typeof entry.restoreEl.focus === "function") {
|
|
4375
|
+
try {
|
|
4376
|
+
entry.restoreEl.focus();
|
|
4377
|
+
} catch {
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
4380
|
+
};
|
|
4381
|
+
},
|
|
4382
|
+
top() {
|
|
4383
|
+
resort();
|
|
4384
|
+
return layers[layers.length - 1] || null;
|
|
4385
|
+
},
|
|
4386
|
+
get scrollLocked() {
|
|
4387
|
+
return locked;
|
|
4388
|
+
},
|
|
4389
|
+
get depth() {
|
|
4390
|
+
return layers.length;
|
|
4391
|
+
}
|
|
4392
|
+
};
|
|
4393
|
+
}
|
|
4394
|
+
const defaultManager = createOverlayManager();
|
|
4395
|
+
const OverlayContext = React.createContext(null);
|
|
4396
|
+
function getOverlayManager() {
|
|
4397
|
+
return defaultManager;
|
|
4398
|
+
}
|
|
4399
|
+
function OverlayProvider({ children }) {
|
|
4400
|
+
const api = React.useMemo(() => createOverlayManager(), []);
|
|
4401
|
+
return /* @__PURE__ */ React.createElement(OverlayContext.Provider, { value: api }, /* @__PURE__ */ React.createElement("div", { id: "cs-overlay-root", "data-cs-overlay-root": "" }), children);
|
|
4402
|
+
}
|
|
4403
|
+
function useOverlayLayer({
|
|
4404
|
+
open,
|
|
4405
|
+
kind = "modal",
|
|
4406
|
+
trapFocus = false,
|
|
4407
|
+
lockScroll,
|
|
4408
|
+
onEscape,
|
|
4409
|
+
panelRef,
|
|
4410
|
+
preferFocusSelector
|
|
4411
|
+
}) {
|
|
4412
|
+
const ctx = React.useContext(OverlayContext);
|
|
4413
|
+
const mgr = ctx || defaultManager;
|
|
4414
|
+
const escapeRef = React.useRef(onEscape);
|
|
4415
|
+
escapeRef.current = onEscape;
|
|
4416
|
+
React.useLayoutEffect(() => {
|
|
4417
|
+
if (!open) return void 0;
|
|
4418
|
+
const restoreEl = typeof document !== "undefined" ? document.activeElement : null;
|
|
4419
|
+
const panel = panelRef && panelRef.current;
|
|
4420
|
+
const unregister = mgr.register({
|
|
4421
|
+
kind,
|
|
4422
|
+
lockScroll: lockScroll != null ? lockScroll : kind === "modal",
|
|
4423
|
+
onEscape: () => escapeRef.current && escapeRef.current(),
|
|
4424
|
+
restoreEl,
|
|
4425
|
+
panelEl: panel || null
|
|
4426
|
+
});
|
|
4427
|
+
let detachTrap = () => {
|
|
4428
|
+
};
|
|
4429
|
+
const top = mgr.top();
|
|
4430
|
+
const isTop = top && top.panelEl === panel;
|
|
4431
|
+
if (trapFocus && panel && isTop) {
|
|
4432
|
+
const preferred = preferFocusSelector && panel.querySelector(preferFocusSelector) || panel.querySelector(focusableSelector) || panel;
|
|
4433
|
+
preferred && preferred.focus && preferred.focus();
|
|
4434
|
+
detachTrap = attachFocusTrap(panel, { handleEscape: false });
|
|
4435
|
+
}
|
|
4436
|
+
return () => {
|
|
4437
|
+
detachTrap();
|
|
4438
|
+
unregister();
|
|
4439
|
+
};
|
|
4440
|
+
}, [open, kind, trapFocus, lockScroll, mgr, panelRef, preferFocusSelector]);
|
|
4441
|
+
return { manager: mgr };
|
|
4442
|
+
}
|
|
4443
|
+
Object.assign(__ds_scope, { focusableSelector, attachFocusTrap, getOverlayManager, OverlayProvider, useOverlayLayer });
|
|
4444
|
+
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/OverlayManager.jsx", error: String((e && e.message) || e) }); }
|
|
4445
|
+
|
|
4446
|
+
// components/dialog/Dialog.jsx
|
|
4447
|
+
try { (() => {
|
|
4448
|
+
const { makeT, useLang } = __ds_scope;
|
|
4449
|
+
const { Icon } = __ds_scope;
|
|
4450
|
+
const { cx } = __ds_scope;
|
|
4451
|
+
const { useOverlayLayer } = __ds_scope;
|
|
4452
|
+
function Dialog({
|
|
4453
|
+
open,
|
|
4454
|
+
title,
|
|
4455
|
+
children,
|
|
4456
|
+
actions,
|
|
4457
|
+
onClose,
|
|
4458
|
+
className,
|
|
4459
|
+
closeLabel,
|
|
4460
|
+
lang,
|
|
4461
|
+
...props
|
|
4462
|
+
}) {
|
|
4463
|
+
const baseId = React.useId();
|
|
4464
|
+
const titleId = baseId + "-title";
|
|
4465
|
+
const bodyId = children == null ? void 0 : baseId + "-body";
|
|
4466
|
+
const [ref, L] = useLang(lang);
|
|
4467
|
+
const panel = React.useRef(null);
|
|
4468
|
+
const closeRef = React.useRef(onClose);
|
|
4469
|
+
closeRef.current = onClose;
|
|
4470
|
+
useOverlayLayer({
|
|
4471
|
+
open: !!open,
|
|
4472
|
+
kind: "modal",
|
|
4473
|
+
trapFocus: true,
|
|
4474
|
+
onEscape: () => closeRef.current && closeRef.current(),
|
|
4475
|
+
panelRef: panel
|
|
4476
|
+
});
|
|
4477
|
+
const cl = closeLabel != null ? closeLabel : makeT("Dialog", L)("close");
|
|
4478
|
+
if (!open) return null;
|
|
4479
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: "cs-dialog-layer" }, /* @__PURE__ */ React.createElement("div", { className: "cs-dialog__overlay", onClick: onClose, "aria-hidden": "true" }), /* @__PURE__ */ React.createElement(
|
|
4480
|
+
"section",
|
|
4481
|
+
{
|
|
4482
|
+
...props,
|
|
4483
|
+
ref: panel,
|
|
4484
|
+
tabIndex: -1,
|
|
4485
|
+
role: "dialog",
|
|
4486
|
+
"aria-modal": "true",
|
|
4487
|
+
"aria-labelledby": titleId,
|
|
4488
|
+
"aria-describedby": bodyId != null ? bodyId : props["aria-describedby"],
|
|
4489
|
+
className: cx("cs-dialog", className)
|
|
4490
|
+
},
|
|
4491
|
+
/* @__PURE__ */ React.createElement("header", { className: "cs-dialog__header" }, /* @__PURE__ */ React.createElement("h2", { id: titleId, className: "cs-dialog__title" }, title), onClose ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--ghost cs-button--sm", onClick: onClose, "aria-label": cl }, /* @__PURE__ */ React.createElement(Icon, { name: "close", size: "sm" })) : null),
|
|
4492
|
+
/* @__PURE__ */ React.createElement("div", { id: bodyId, className: "cs-dialog__body" }, children),
|
|
4493
|
+
actions ? /* @__PURE__ */ React.createElement("footer", { className: "cs-dialog__actions" }, actions) : null
|
|
4494
|
+
));
|
|
4495
|
+
}
|
|
4496
|
+
Object.assign(__ds_scope, { Dialog });
|
|
4497
|
+
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/dialog/Dialog.jsx", error: String((e && e.message) || e) }); }
|
|
4498
|
+
|
|
4499
|
+
// components/navigation/CommandPalette.jsx
|
|
4500
|
+
try { (() => {
|
|
4501
|
+
const { makeT, useLang } = __ds_scope;
|
|
4502
|
+
const { cx } = __ds_scope;
|
|
4503
|
+
const { useOverlayLayer } = __ds_scope;
|
|
4504
|
+
function CommandPalette({ open, onClose, placeholder, groups = [], lang, className }) {
|
|
4505
|
+
const [q, setQ] = React.useState("");
|
|
4506
|
+
const panel = React.useRef(null);
|
|
4507
|
+
const closeRef = React.useRef(onClose);
|
|
4508
|
+
closeRef.current = onClose;
|
|
4509
|
+
useOverlayLayer({
|
|
4510
|
+
open: !!open,
|
|
4511
|
+
kind: "modal",
|
|
4512
|
+
trapFocus: true,
|
|
4513
|
+
preferFocusSelector: ".cs-cmdk__search input",
|
|
4514
|
+
onEscape: () => closeRef.current && closeRef.current(),
|
|
4515
|
+
panelRef: panel
|
|
4516
|
+
});
|
|
4517
|
+
const [ref, L] = useLang(lang);
|
|
4518
|
+
const t = makeT("CommandPalette", L);
|
|
4519
|
+
const ph = placeholder != null ? placeholder : t("placeholder");
|
|
4520
|
+
if (!open) return null;
|
|
4521
|
+
const needle = q.trim().toLowerCase();
|
|
4522
|
+
return /* @__PURE__ */ React.createElement(
|
|
4523
|
+
"div",
|
|
4524
|
+
{
|
|
4525
|
+
ref: (el) => {
|
|
4526
|
+
ref.current = el;
|
|
4527
|
+
panel.current = el;
|
|
4528
|
+
},
|
|
4529
|
+
className: "cs-cmdk-scrim",
|
|
4530
|
+
onClick: onClose
|
|
4531
|
+
},
|
|
4532
|
+
/* @__PURE__ */ React.createElement("div", { className: cx("cs-cmdk", className), role: "dialog", "aria-modal": "true", "aria-label": t("aria"), onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__search" }, /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("circle", { cx: "11", cy: "11", r: "7" }), /* @__PURE__ */ React.createElement("path", { d: "M21 21l-4.3-4.3" })), /* @__PURE__ */ React.createElement("input", { autoFocus: true, value: q, onChange: (e) => setQ(e.target.value), placeholder: ph }), /* @__PURE__ */ React.createElement("span", { className: "cs-kbd" }, t("esc"))), /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__list" }, groups.map((g, gi) => {
|
|
4533
|
+
const items = (g.items || []).filter((it) => !needle || String(it.label).toLowerCase().includes(needle));
|
|
4534
|
+
if (!items.length) return null;
|
|
4535
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: gi }, g.label ? /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__label" }, g.label) : null, items.map((it, ii) => /* @__PURE__ */ React.createElement("button", { key: ii, type: "button", className: "cs-cmdk__item", onClick: () => {
|
|
4536
|
+
it.onSelect && it.onSelect();
|
|
4537
|
+
onClose && onClose();
|
|
4538
|
+
} }, it.icon ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, it.icon) : null, /* @__PURE__ */ React.createElement("span", null, it.label), it.shortcut ? /* @__PURE__ */ React.createElement("span", { className: "cs-kbd" }, it.shortcut) : null)));
|
|
4539
|
+
})))
|
|
4540
|
+
);
|
|
4541
|
+
}
|
|
4542
|
+
Object.assign(__ds_scope, { CommandPalette });
|
|
4543
|
+
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/CommandPalette.jsx", error: String((e && e.message) || e) }); }
|
|
4544
|
+
|
|
4545
|
+
// components/overlays/AlertDialog.jsx
|
|
4546
|
+
try { (() => {
|
|
4547
|
+
const { makeT, useLang } = __ds_scope;
|
|
4548
|
+
const { cx } = __ds_scope;
|
|
4549
|
+
const { useOverlayLayer } = __ds_scope;
|
|
3608
4550
|
function AlertDialog({
|
|
3609
4551
|
open,
|
|
3610
4552
|
defaultOpen = false,
|
|
@@ -3640,45 +4582,19 @@ function AlertDialog({
|
|
|
3640
4582
|
confirmRef.current = onConfirm;
|
|
3641
4583
|
const cancelRef = React.useRef(onCancel);
|
|
3642
4584
|
cancelRef.current = onCancel;
|
|
3643
|
-
React.
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
(
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
}
|
|
3657
|
-
if (e.key !== "Tab" || !panel.current) return;
|
|
3658
|
-
const f = focusables();
|
|
3659
|
-
if (!f.length) {
|
|
3660
|
-
e.preventDefault();
|
|
3661
|
-
panel.current.focus();
|
|
3662
|
-
return;
|
|
3663
|
-
}
|
|
3664
|
-
const a = f[0], z = f[f.length - 1];
|
|
3665
|
-
const active = document.activeElement;
|
|
3666
|
-
const inside = panel.current.contains(active);
|
|
3667
|
-
if (e.shiftKey && (!inside || active === a)) {
|
|
3668
|
-
e.preventDefault();
|
|
3669
|
-
z.focus();
|
|
3670
|
-
} else if (!e.shiftKey && (!inside || active === z)) {
|
|
3671
|
-
e.preventDefault();
|
|
3672
|
-
a.focus();
|
|
3673
|
-
}
|
|
3674
|
-
};
|
|
3675
|
-
document.addEventListener("keydown", k);
|
|
3676
|
-
return () => {
|
|
3677
|
-
document.removeEventListener("keydown", k);
|
|
3678
|
-
document.body.style.overflow = prevOverflow;
|
|
3679
|
-
if (prev && prev.focus) prev.focus();
|
|
3680
|
-
};
|
|
3681
|
-
}, [isOpen]);
|
|
4585
|
+
const setOpenRef = React.useRef(setOpen);
|
|
4586
|
+
setOpenRef.current = setOpen;
|
|
4587
|
+
useOverlayLayer({
|
|
4588
|
+
open: isOpen,
|
|
4589
|
+
kind: "modal",
|
|
4590
|
+
trapFocus: true,
|
|
4591
|
+
preferFocusSelector: ".cs-alert-dialog__confirm",
|
|
4592
|
+
onEscape: () => {
|
|
4593
|
+
setOpenRef.current(false);
|
|
4594
|
+
cancelRef.current?.();
|
|
4595
|
+
},
|
|
4596
|
+
panelRef: panel
|
|
4597
|
+
});
|
|
3682
4598
|
const cl = cancelLabel != null ? cancelLabel : t("cancel");
|
|
3683
4599
|
const cf = confirmLabel != null ? confirmLabel : t("confirm");
|
|
3684
4600
|
const body = description ?? children;
|
|
@@ -3718,134 +4634,69 @@ function AlertDialog({
|
|
|
3718
4634
|
cancelRef.current?.();
|
|
3719
4635
|
}
|
|
3720
4636
|
},
|
|
3721
|
-
cl
|
|
3722
|
-
), /* @__PURE__ */ React.createElement(
|
|
3723
|
-
"button",
|
|
3724
|
-
{
|
|
3725
|
-
type: "button",
|
|
3726
|
-
className: cx(
|
|
3727
|
-
"cs-button",
|
|
3728
|
-
"cs-button--md",
|
|
3729
|
-
"cs-alert-dialog__confirm",
|
|
3730
|
-
destructive ? "cs-button--danger" : "cs-button--primary"
|
|
3731
|
-
),
|
|
3732
|
-
onClick: () => {
|
|
3733
|
-
setOpen(false);
|
|
3734
|
-
confirmRef.current?.();
|
|
3735
|
-
}
|
|
3736
|
-
},
|
|
3737
|
-
cf
|
|
3738
|
-
))
|
|
3739
|
-
));
|
|
3740
|
-
}
|
|
3741
|
-
Object.assign(__ds_scope, { AlertDialog });
|
|
3742
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/AlertDialog.jsx", error: String((e && e.message) || e) }); }
|
|
3743
|
-
|
|
3744
|
-
// components/overlays/ContextMenu.jsx
|
|
3745
|
-
try { (() => {
|
|
3746
|
-
const { cx } = __ds_scope;
|
|
3747
|
-
function ContextMenu({ items = [], children, className }) {
|
|
3748
|
-
const [pos, setPos] = React.useState(null);
|
|
3749
|
-
React.useEffect(() => {
|
|
3750
|
-
if (!pos) return;
|
|
3751
|
-
const close = () => setPos(null);
|
|
3752
|
-
const k = (e) => {
|
|
3753
|
-
if (e.key === "Escape") setPos(null);
|
|
3754
|
-
};
|
|
3755
|
-
document.addEventListener("click", close);
|
|
3756
|
-
document.addEventListener("keydown", k);
|
|
3757
|
-
return () => {
|
|
3758
|
-
document.removeEventListener("click", close);
|
|
3759
|
-
document.removeEventListener("keydown", k);
|
|
3760
|
-
};
|
|
3761
|
-
}, [pos]);
|
|
3762
|
-
return /* @__PURE__ */ React.createElement(
|
|
3763
|
-
"div",
|
|
3764
|
-
{
|
|
3765
|
-
className: cx("cs-ctxmenu-zone", className),
|
|
3766
|
-
onContextMenu: (e) => {
|
|
3767
|
-
e.preventDefault();
|
|
3768
|
-
const r = e.currentTarget.getBoundingClientRect();
|
|
3769
|
-
setPos({ x: e.clientX - r.left, y: e.clientY - r.top });
|
|
3770
|
-
}
|
|
3771
|
-
},
|
|
3772
|
-
children,
|
|
3773
|
-
pos ? /* @__PURE__ */ React.createElement("div", { className: "cs-menu__list", role: "menu", style: { position: "absolute", insetInlineStart: pos.x, insetBlockStart: pos.y } }, items.map((it, i) => it === "-" ? /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-menu__sep" }) : /* @__PURE__ */ React.createElement("button", { key: i, type: "button", role: "menuitem", className: cx("cs-menu__item", it.danger && "cs-menu__item--danger"), onClick: () => {
|
|
3774
|
-
setPos(null);
|
|
3775
|
-
it.onSelect && it.onSelect();
|
|
3776
|
-
} }, it.label))) : null
|
|
3777
|
-
);
|
|
4637
|
+
cl
|
|
4638
|
+
), /* @__PURE__ */ React.createElement(
|
|
4639
|
+
"button",
|
|
4640
|
+
{
|
|
4641
|
+
type: "button",
|
|
4642
|
+
className: cx(
|
|
4643
|
+
"cs-button",
|
|
4644
|
+
"cs-button--md",
|
|
4645
|
+
"cs-alert-dialog__confirm",
|
|
4646
|
+
destructive ? "cs-button--danger" : "cs-button--primary"
|
|
4647
|
+
),
|
|
4648
|
+
onClick: () => {
|
|
4649
|
+
setOpen(false);
|
|
4650
|
+
confirmRef.current?.();
|
|
4651
|
+
}
|
|
4652
|
+
},
|
|
4653
|
+
cf
|
|
4654
|
+
))
|
|
4655
|
+
));
|
|
3778
4656
|
}
|
|
3779
|
-
Object.assign(__ds_scope, {
|
|
3780
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/
|
|
4657
|
+
Object.assign(__ds_scope, { AlertDialog });
|
|
4658
|
+
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/AlertDialog.jsx", error: String((e && e.message) || e) }); }
|
|
3781
4659
|
|
|
3782
4660
|
// components/overlays/Drawer.jsx
|
|
3783
4661
|
try { (() => {
|
|
3784
4662
|
const { makeT, useLang } = __ds_scope;
|
|
3785
4663
|
const { cx } = __ds_scope;
|
|
4664
|
+
const { useOverlayLayer } = __ds_scope;
|
|
3786
4665
|
function Drawer({ open, onClose, title, side = "right", children, actions, lang, className }) {
|
|
3787
4666
|
const [ref, L] = useLang(lang);
|
|
3788
|
-
React.
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
return;
|
|
3799
|
-
}
|
|
3800
|
-
if (e.key !== "Tab" || !ref.current) return;
|
|
3801
|
-
const f = [...ref.current.querySelectorAll(SEL)];
|
|
3802
|
-
if (!f.length) return;
|
|
3803
|
-
const a = f[0], z = f[f.length - 1];
|
|
3804
|
-
if (e.shiftKey && document.activeElement === a) {
|
|
3805
|
-
e.preventDefault();
|
|
3806
|
-
z.focus();
|
|
3807
|
-
} else if (!e.shiftKey && document.activeElement === z) {
|
|
3808
|
-
e.preventDefault();
|
|
3809
|
-
a.focus();
|
|
3810
|
-
}
|
|
3811
|
-
};
|
|
3812
|
-
document.addEventListener("keydown", k);
|
|
3813
|
-
return () => {
|
|
3814
|
-
document.removeEventListener("keydown", k);
|
|
3815
|
-
if (prev && prev.focus) prev.focus();
|
|
3816
|
-
};
|
|
3817
|
-
}, [open]);
|
|
4667
|
+
const panel = React.useRef(null);
|
|
4668
|
+
const closeRef = React.useRef(onClose);
|
|
4669
|
+
closeRef.current = onClose;
|
|
4670
|
+
useOverlayLayer({
|
|
4671
|
+
open: !!open,
|
|
4672
|
+
kind: "modal",
|
|
4673
|
+
trapFocus: true,
|
|
4674
|
+
onEscape: () => closeRef.current && closeRef.current(),
|
|
4675
|
+
panelRef: panel
|
|
4676
|
+
});
|
|
3818
4677
|
const t = makeT("Drawer", L);
|
|
3819
4678
|
if (!open) return null;
|
|
3820
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "cs-drawer-scrim", onClick: onClose, "aria-hidden": "true" }), /* @__PURE__ */ React.createElement(
|
|
4679
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "cs-drawer-scrim", onClick: onClose, "aria-hidden": "true" }), /* @__PURE__ */ React.createElement(
|
|
4680
|
+
"aside",
|
|
4681
|
+
{
|
|
4682
|
+
ref: (el) => {
|
|
4683
|
+
panel.current = el;
|
|
4684
|
+
ref.current = el;
|
|
4685
|
+
},
|
|
4686
|
+
tabIndex: -1,
|
|
4687
|
+
className: cx("cs-drawer", side === "left" && "cs-drawer--left", className),
|
|
4688
|
+
role: "dialog",
|
|
4689
|
+
"aria-modal": "true",
|
|
4690
|
+
"aria-label": typeof title === "string" ? title : t("panel")
|
|
4691
|
+
},
|
|
4692
|
+
/* @__PURE__ */ React.createElement("div", { className: "cs-drawer__header" }, title ? /* @__PURE__ */ React.createElement("h2", { className: "cs-drawer__title" }, title) : null, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-drawer__close", "aria-label": t("close"), onClick: onClose }, /* @__PURE__ */ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 6l12 12M18 6L6 18" })))),
|
|
4693
|
+
/* @__PURE__ */ React.createElement("div", { className: "cs-drawer__body", tabIndex: 0 }, children),
|
|
4694
|
+
actions ? /* @__PURE__ */ React.createElement("div", { className: "cs-drawer__footer" }, actions) : null
|
|
4695
|
+
));
|
|
3821
4696
|
}
|
|
3822
4697
|
Object.assign(__ds_scope, { Drawer });
|
|
3823
4698
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/Drawer.jsx", error: String((e && e.message) || e) }); }
|
|
3824
4699
|
|
|
3825
|
-
// components/overlays/HoverCard.jsx
|
|
3826
|
-
try { (() => {
|
|
3827
|
-
const { cx } = __ds_scope;
|
|
3828
|
-
function HoverCard({ trigger, children, openDelay = 150, closeDelay = 200, className }) {
|
|
3829
|
-
const [open, setOpen] = React.useState(false);
|
|
3830
|
-
const t1 = React.useRef();
|
|
3831
|
-
const t2 = React.useRef();
|
|
3832
|
-
const show = () => {
|
|
3833
|
-
clearTimeout(t2.current);
|
|
3834
|
-
t1.current = setTimeout(() => setOpen(true), openDelay);
|
|
3835
|
-
};
|
|
3836
|
-
const hide = () => {
|
|
3837
|
-
clearTimeout(t1.current);
|
|
3838
|
-
t2.current = setTimeout(() => setOpen(false), closeDelay);
|
|
3839
|
-
};
|
|
3840
|
-
React.useEffect(() => () => {
|
|
3841
|
-
clearTimeout(t1.current);
|
|
3842
|
-
clearTimeout(t2.current);
|
|
3843
|
-
}, []);
|
|
3844
|
-
return /* @__PURE__ */ React.createElement("span", { className: cx("cs-hovercard", className), onMouseEnter: show, onMouseLeave: hide, onFocus: show, onBlur: hide }, trigger, open ? /* @__PURE__ */ React.createElement("span", { className: "cs-hovercard__panel", role: "dialog" }, children) : null);
|
|
3845
|
-
}
|
|
3846
|
-
Object.assign(__ds_scope, { HoverCard });
|
|
3847
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/HoverCard.jsx", error: String((e && e.message) || e) }); }
|
|
3848
|
-
|
|
3849
4700
|
// components/overlays/Popconfirm.jsx
|
|
3850
4701
|
try { (() => {
|
|
3851
4702
|
const { makeT, useLang } = __ds_scope;
|
|
@@ -3937,30 +4788,37 @@ const { cx } = __ds_scope;
|
|
|
3937
4788
|
function Tour({ steps = [], open, onClose, lang, className }) {
|
|
3938
4789
|
const [i, setI] = React.useState(0);
|
|
3939
4790
|
const [rect, setRect] = React.useState(null);
|
|
4791
|
+
const [pop, setPop] = React.useState({ top: 80, left: 40 });
|
|
3940
4792
|
const [ref, L] = useLang(lang);
|
|
3941
4793
|
const t = makeT("Tour", L);
|
|
3942
4794
|
React.useEffect(() => {
|
|
3943
4795
|
if (open) setI(0);
|
|
3944
4796
|
}, [open]);
|
|
3945
4797
|
React.useEffect(() => {
|
|
3946
|
-
if (!open || !steps[i]) return;
|
|
4798
|
+
if (!open || !steps[i]) return void 0;
|
|
3947
4799
|
const el = document.querySelector(steps[i].target);
|
|
3948
4800
|
if (el) {
|
|
3949
4801
|
const r = el.getBoundingClientRect();
|
|
3950
|
-
|
|
3951
|
-
|
|
4802
|
+
const next = { x: r.left - 6, y: r.top - 6, w: r.width + 12, h: r.height + 12 };
|
|
4803
|
+
setRect(next);
|
|
4804
|
+
setPop({
|
|
4805
|
+
top: Math.min(window.innerHeight - 170, next.y + next.h + 12),
|
|
4806
|
+
left: Math.max(12, Math.min(window.innerWidth - 292, next.x))
|
|
4807
|
+
});
|
|
4808
|
+
} else {
|
|
4809
|
+
setRect(null);
|
|
4810
|
+
setPop({ top: 80, left: 40 });
|
|
4811
|
+
}
|
|
3952
4812
|
const k = (e) => {
|
|
3953
4813
|
if (e.key === "Escape") onClose && onClose();
|
|
3954
4814
|
};
|
|
3955
4815
|
document.addEventListener("keydown", k);
|
|
3956
4816
|
return () => document.removeEventListener("keydown", k);
|
|
3957
|
-
}, [open, i, steps]);
|
|
4817
|
+
}, [open, i, steps, onClose]);
|
|
3958
4818
|
if (!open || !steps.length) return /* @__PURE__ */ React.createElement("span", { ref, style: { display: "none" } });
|
|
3959
4819
|
const s = steps[i];
|
|
3960
4820
|
const last = i === steps.length - 1;
|
|
3961
|
-
|
|
3962
|
-
const popLeft = rect ? Math.max(12, Math.min(window.innerWidth - 292, rect.x)) : 40;
|
|
3963
|
-
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-tour", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__scrim", onClick: onClose }), rect ? /* @__PURE__ */ React.createElement("div", { className: "cs-tour__hole", style: { left: rect.x, top: rect.y, width: rect.w, height: rect.h } }) : null, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__pop", role: "dialog", "aria-label": typeof s.title === "string" ? s.title : void 0, style: { left: popLeft, top: popTop } }, /* @__PURE__ */ React.createElement("b", null, s.title), s.body ? /* @__PURE__ */ React.createElement("p", null, s.body) : null, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__bar" }, /* @__PURE__ */ React.createElement("span", { className: "cs-tour__count" }, i + 1 + " / " + steps.length), /* @__PURE__ */ React.createElement("span", { className: "cs-tour__btns" }, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--ghost cs-button--xs", onClick: onClose }, t("skip")), i > 0 ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--secondary cs-button--xs", onClick: () => setI(i - 1) }, t("back")) : null, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--primary cs-button--xs", onClick: () => last ? onClose && onClose() : setI(i + 1) }, last ? t("done") : t("next"))))));
|
|
4821
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-tour", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__scrim", onClick: onClose }), rect ? /* @__PURE__ */ React.createElement("div", { className: "cs-tour__hole", style: { left: rect.x, top: rect.y, width: rect.w, height: rect.h } }) : null, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__pop", role: "dialog", "aria-label": typeof s.title === "string" ? s.title : void 0, style: { left: pop.left, top: pop.top } }, /* @__PURE__ */ React.createElement("b", null, s.title), s.body ? /* @__PURE__ */ React.createElement("p", null, s.body) : null, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__bar" }, /* @__PURE__ */ React.createElement("span", { className: "cs-tour__count" }, i + 1 + " / " + steps.length), /* @__PURE__ */ React.createElement("span", { className: "cs-tour__btns" }, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--ghost cs-button--xs", onClick: onClose }, t("skip")), i > 0 ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--secondary cs-button--xs", onClick: () => setI(i - 1) }, t("back")) : null, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--primary cs-button--xs", onClick: () => last ? onClose && onClose() : setI(i + 1) }, last ? t("done") : t("next"))))));
|
|
3964
4822
|
}
|
|
3965
4823
|
Object.assign(__ds_scope, { Tour });
|
|
3966
4824
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/Tour.jsx", error: String((e && e.message) || e) }); }
|
|
@@ -4011,733 +4869,12 @@ function TextField({
|
|
|
4011
4869
|
Object.assign(__ds_scope, { TextField });
|
|
4012
4870
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/textfield/TextField.jsx", error: String((e && e.message) || e) }); }
|
|
4013
4871
|
|
|
4014
|
-
// image-slot.js
|
|
4015
|
-
try { (() => {
|
|
4016
|
-
(() => {
|
|
4017
|
-
const STATE_FILE = ".image-slots.state.json";
|
|
4018
|
-
const UNSPLASH_HOMEPAGE_HREF = "https://unsplash.com/?utm_source=claude_design&utm_medium=referral";
|
|
4019
|
-
const isUnsplashHost = (u) => {
|
|
4020
|
-
try {
|
|
4021
|
-
return /(^|\.)unsplash\.com$/.test(
|
|
4022
|
-
new URL(u, document.baseURI).hostname.replace(/\.$/, "")
|
|
4023
|
-
);
|
|
4024
|
-
} catch {
|
|
4025
|
-
return false;
|
|
4026
|
-
}
|
|
4027
|
-
};
|
|
4028
|
-
const withReferral = (href) => {
|
|
4029
|
-
try {
|
|
4030
|
-
const u = new URL(href);
|
|
4031
|
-
if (!/(^|\.)unsplash\.com$/.test(u.hostname.replace(/\.$/, ""))) {
|
|
4032
|
-
return href;
|
|
4033
|
-
}
|
|
4034
|
-
if (!u.searchParams.has("utm_source")) {
|
|
4035
|
-
u.searchParams.set("utm_source", "claude_design");
|
|
4036
|
-
}
|
|
4037
|
-
if (!u.searchParams.has("utm_medium")) {
|
|
4038
|
-
u.searchParams.set("utm_medium", "referral");
|
|
4039
|
-
}
|
|
4040
|
-
return u.toString();
|
|
4041
|
-
} catch (e) {
|
|
4042
|
-
return href;
|
|
4043
|
-
}
|
|
4044
|
-
};
|
|
4045
|
-
const MAX_DIM = 1200;
|
|
4046
|
-
const ACCEPT = ["image/png", "image/jpeg", "image/webp", "image/avif"];
|
|
4047
|
-
const subs = /* @__PURE__ */ new Set();
|
|
4048
|
-
let slots = {};
|
|
4049
|
-
const tombstones = /* @__PURE__ */ new Set();
|
|
4050
|
-
let loaded = false;
|
|
4051
|
-
let loadP = null;
|
|
4052
|
-
function load() {
|
|
4053
|
-
if (loadP) return loadP;
|
|
4054
|
-
loadP = fetch(STATE_FILE).then((r) => r.ok ? r.json() : null).then((j) => {
|
|
4055
|
-
if (j && typeof j === "object") {
|
|
4056
|
-
const merged = Object.assign({}, j, slots);
|
|
4057
|
-
for (const k in slots) {
|
|
4058
|
-
if (merged[k] && !merged[k].u && j[k]) {
|
|
4059
|
-
merged[k].u = typeof j[k] === "string" ? j[k] : j[k].u;
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
for (const id of tombstones) delete merged[id];
|
|
4063
|
-
slots = merged;
|
|
4064
|
-
}
|
|
4065
|
-
tombstones.clear();
|
|
4066
|
-
}).catch(() => {
|
|
4067
|
-
}).then(() => {
|
|
4068
|
-
loaded = true;
|
|
4069
|
-
subs.forEach((fn) => fn());
|
|
4070
|
-
});
|
|
4071
|
-
return loadP;
|
|
4072
|
-
}
|
|
4073
|
-
let saving = false;
|
|
4074
|
-
let saveDirty = false;
|
|
4075
|
-
function flushNow() {
|
|
4076
|
-
if (!loaded) return;
|
|
4077
|
-
const w = window.omelette && window.omelette.writeFile;
|
|
4078
|
-
if (!w) return;
|
|
4079
|
-
try {
|
|
4080
|
-
Promise.resolve(w(STATE_FILE, JSON.stringify(slots))).catch(() => {
|
|
4081
|
-
});
|
|
4082
|
-
} catch (e) {
|
|
4083
|
-
}
|
|
4084
|
-
}
|
|
4085
|
-
function save() {
|
|
4086
|
-
if (saving) {
|
|
4087
|
-
saveDirty = true;
|
|
4088
|
-
return;
|
|
4089
|
-
}
|
|
4090
|
-
const w = window.omelette && window.omelette.writeFile;
|
|
4091
|
-
if (!w) return;
|
|
4092
|
-
saving = true;
|
|
4093
|
-
Promise.resolve(w(STATE_FILE, JSON.stringify(slots))).catch(() => {
|
|
4094
|
-
}).then(() => {
|
|
4095
|
-
saving = false;
|
|
4096
|
-
if (saveDirty) {
|
|
4097
|
-
saveDirty = false;
|
|
4098
|
-
save();
|
|
4099
|
-
}
|
|
4100
|
-
});
|
|
4101
|
-
}
|
|
4102
|
-
const S_MAX = 5;
|
|
4103
|
-
const clampS = (s) => Math.max(1, Math.min(S_MAX, s));
|
|
4104
|
-
function getSlot(id) {
|
|
4105
|
-
const v = slots[id];
|
|
4106
|
-
if (!v) return null;
|
|
4107
|
-
return typeof v === "string" ? { u: v, s: 1, x: 0, y: 0 } : v;
|
|
4108
|
-
}
|
|
4109
|
-
function setSlot(id, val) {
|
|
4110
|
-
if (!id) return;
|
|
4111
|
-
if (val) {
|
|
4112
|
-
slots[id] = val;
|
|
4113
|
-
tombstones.delete(id);
|
|
4114
|
-
} else {
|
|
4115
|
-
delete slots[id];
|
|
4116
|
-
if (!loaded) tombstones.add(id);
|
|
4117
|
-
}
|
|
4118
|
-
subs.forEach((fn) => fn());
|
|
4119
|
-
if (loaded) save();
|
|
4120
|
-
else load().then(save);
|
|
4121
|
-
}
|
|
4122
|
-
async function toDataUrl(file, targetW) {
|
|
4123
|
-
const bitmap = await createImageBitmap(file);
|
|
4124
|
-
try {
|
|
4125
|
-
const cap = Math.min(MAX_DIM, Math.max(1, Math.round(targetW * 2)) || MAX_DIM);
|
|
4126
|
-
const scale = Math.min(1, cap / Math.max(bitmap.width, bitmap.height));
|
|
4127
|
-
const w = Math.max(1, Math.round(bitmap.width * scale));
|
|
4128
|
-
const h = Math.max(1, Math.round(bitmap.height * scale));
|
|
4129
|
-
const canvas = document.createElement("canvas");
|
|
4130
|
-
canvas.width = w;
|
|
4131
|
-
canvas.height = h;
|
|
4132
|
-
canvas.getContext("2d").drawImage(bitmap, 0, 0, w, h);
|
|
4133
|
-
return canvas.toDataURL("image/webp", 0.85);
|
|
4134
|
-
} finally {
|
|
4135
|
-
bitmap.close && bitmap.close();
|
|
4136
|
-
}
|
|
4137
|
-
}
|
|
4138
|
-
const stylesheet = (
|
|
4139
|
-
// Fill the container by default: slots are usually placed inside a
|
|
4140
|
-
// sized wrapper (a hero frame, a grid cell, an inset:0 layer) and are
|
|
4141
|
-
// expected to take that box — a fixed intrinsic size would render as
|
|
4142
|
-
// a small tile in the corner of a full-bleed wrapper instead.
|
|
4143
|
-
// aspect-ratio is the companion fallback that keeps a bare slot
|
|
4144
|
-
// visible when the parent's height is indefinite: height:100%
|
|
4145
|
-
// resolves to auto there, and the ratio then derives height from
|
|
4146
|
-
// width instead of letting the slot collapse to zero height.
|
|
4147
|
-
// Explicit width/height on the element override all of this.
|
|
4148
|
-
':host{display:block;position:relative; font:13px/1.3 system-ui,-apple-system,sans-serif;color:rgba(0,0,0,.55); width:100%;height:100%;aspect-ratio:3/2}.frame{position:absolute;inset:0;overflow:hidden;background:rgba(0,0,0,.04)}.frame img{position:absolute;max-width:none;transform:translate(-50%,-50%); -webkit-user-drag:none;user-select:none;touch-action:none}.spill{position:fixed;margin:0;inset:auto;border:0;padding:0;background:transparent; overflow:visible;transform:translate(-50%,-50%);z-index:1;cursor:grab;touch-action:none}:host([data-panning]) .spill{cursor:grabbing}.spill .ghost{position:absolute;inset:0;width:100%;height:100%;opacity:.35; pointer-events:none;-webkit-user-drag:none;user-select:none; box-shadow:0 0 0 1px rgba(0,0,0,.2),0 12px 32px rgba(0,0,0,.2)}.spill .handle{position:absolute;width:12px;height:12px;border-radius:50%; background:#fff;box-shadow:0 0 0 1.5px #c96442,0 1px 3px rgba(0,0,0,.3); transform:translate(-50%,-50%)}.spill .handle[data-c=nw]{left:0;top:0;cursor:nwse-resize}.spill .handle[data-c=ne]{left:100%;top:0;cursor:nesw-resize}.spill .handle[data-c=sw]{left:0;top:100%;cursor:nesw-resize}.spill .handle[data-c=se]{left:100%;top:100%;cursor:nwse-resize}:host([data-reframe]){z-index:10}:host([data-reframe]) .frame{box-shadow:0 0 0 2px #c96442}.empty{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center; justify-content:center;gap:6px;text-align:center;padding:12px;box-sizing:border-box; cursor:pointer;user-select:none}.empty svg{opacity:.45}.empty .cap{max-width:90%;font-weight:500;letter-spacing:.01em}.empty .sub{font-size:11px}.empty .sub u{text-underline-offset:2px;text-decoration-color:rgba(0,0,0,.25)}.empty:hover .sub u{color:rgba(0,0,0,.75);text-decoration-color:currentColor}:host([data-over]) .frame{outline:2px solid #c96442;outline-offset:-2px; background:rgba(201,100,66,.10)}.ring{position:absolute;inset:0;pointer-events:none;border:1.5px dashed rgba(0,0,0,.25); transition:border-color .12s}:host([data-over]) .ring{border-color:#c96442}:host([data-filled]) .ring{display:none}.ctl{position:absolute;inset:auto;top:8px;right:8px;margin:0;border:0;padding:0; background:transparent;overflow:visible; display:flex;gap:6px;opacity:0;pointer-events:none;transition:opacity .12s;z-index:2; white-space:nowrap}.ctl:popover-open{position:fixed;inset:auto;transform:translateX(-100%)}:host([data-filled][data-editable]:hover) .ctl,:host([data-reframe]) .ctl {opacity:1;pointer-events:auto}.ctl button{appearance:none;border:0;border-radius:6px;padding:5px 10px;cursor:pointer; background:rgba(0,0,0,.65);color:#fff;font:11px/1 system-ui,-apple-system,sans-serif; backdrop-filter:blur(6px)}.ctl button:hover{background:rgba(0,0,0,.8)}.err{position:absolute;left:8px;bottom:8px;right:8px;color:#b3261e;font-size:11px; background:rgba(255,255,255,.85);padding:4px 6px;border-radius:5px;pointer-events:none}:host([data-swapping]) .frame img{visibility:hidden}.loading{position:absolute;inset:0;display:none;align-items:center; justify-content:center;pointer-events:none}:host([data-swapping]) .loading{display:flex}.loading::after{content:"";width:22px;height:22px;border-radius:50%; border:2px solid rgba(0,0,0,.12);border-top-color:rgba(0,0,0,.45); animation:om-slot-spin .7s linear infinite}@keyframes om-slot-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.loading::after{animation:none}}.credit{position:absolute;left:6px;bottom:6px;max-width:calc(100% - 12px);display:none; padding:3px 7px;border-radius:5px;background:rgba(0,0,0,.55);color:#fff; font:10px/1.2 system-ui,-apple-system,sans-serif;text-decoration:none; white-space:nowrap;overflow:hidden;text-overflow:ellipsis;backdrop-filter:blur(6px)}.credit a{color:inherit;text-decoration:none}.credit a:hover,.credit a:focus-visible{text-decoration:underline}:host([data-filled][data-credit]) .credit{display:block}:host-context([data-om-exporting]) .ctl,:host-context([data-om-exporting]) .credit{display:none !important}.attr-error{position:absolute;inset:0;display:none;flex-direction:column;align-items:center; justify-content:center;gap:6px;text-align:center;padding:12px;box-sizing:border-box; background:#f2f1ef;color:#6e6c66;user-select:none; font:13px/1.45 system-ui,-apple-system,sans-serif}.attr-error svg{opacity:.55}.attr-error .cap{max-width:92%;font-weight:500;letter-spacing:.01em}:host([data-attribution-error]) .attr-error{display:flex}:host([data-attribution-error]) .ring{display:none}'
|
|
4149
|
-
);
|
|
4150
|
-
const icon = '<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg>';
|
|
4151
|
-
const warnIcon = '<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>';
|
|
4152
|
-
class ImageSlot extends HTMLElement {
|
|
4153
|
-
static get observedAttributes() {
|
|
4154
|
-
return ["shape", "radius", "mask", "fit", "placeholder", "src", "id", "credit", "credit-href"];
|
|
4155
|
-
}
|
|
4156
|
-
/** Duplicate-slide hook (called by deck-stage, see its
|
|
4157
|
-
* _remintDuplicateIds): copy this id's stored image, if any, under a
|
|
4158
|
-
* freshly minted key and return that key — so a duplicated slide's
|
|
4159
|
-
* slot keeps its dropped photo instead of reverting to the
|
|
4160
|
-
* placeholder. 'isFree' is the caller's uniqueness check (document
|
|
4161
|
-
* ids); candidates must ALSO be unused in the sidecar, which can
|
|
4162
|
-
* hold keys from other pages sharing the project root. (An EMPTY
|
|
4163
|
-
* slot on another page leaves no sidecar entry, so its id is not
|
|
4164
|
-
* detectable here — a minted key can collide with it and that slot
|
|
4165
|
-
* would show this photo. Same blast radius as two pages reusing an
|
|
4166
|
-
* id by hand, which the shared sidecar already permits.) Returns null
|
|
4167
|
-
* when no id could be minted (caller strips the id, today's
|
|
4168
|
-
* behavior). */
|
|
4169
|
-
static cloneSlot(fromId, isFree) {
|
|
4170
|
-
if (typeof fromId !== "string" || !fromId) return null;
|
|
4171
|
-
if (!loaded) return null;
|
|
4172
|
-
const stem = fromId.replace(/-\d+$/, "") || fromId;
|
|
4173
|
-
for (let n = 2; n < 100; n++) {
|
|
4174
|
-
const toId = stem + "-" + n;
|
|
4175
|
-
if (toId === fromId) continue;
|
|
4176
|
-
if (slots[toId] !== void 0) {
|
|
4177
|
-
const prev = getSlot(toId);
|
|
4178
|
-
const cur = getSlot(fromId);
|
|
4179
|
-
if (!(prev && cur && prev.u && prev.u === cur.u && prev.s === cur.s && prev.x === cur.x && prev.y === cur.y && (typeof isFree !== "function" || isFree(toId)))) continue;
|
|
4180
|
-
return toId;
|
|
4181
|
-
}
|
|
4182
|
-
if (typeof isFree === "function" && !isFree(toId)) continue;
|
|
4183
|
-
const v = getSlot(fromId);
|
|
4184
|
-
if (v) setSlot(toId, Object.assign({}, v));
|
|
4185
|
-
return toId;
|
|
4186
|
-
}
|
|
4187
|
-
return null;
|
|
4188
|
-
}
|
|
4189
|
-
constructor() {
|
|
4190
|
-
super();
|
|
4191
|
-
const root = this.shadowRoot || this.attachShadow({ mode: "open", clonable: true });
|
|
4192
|
-
root.innerHTML = "<style>" + stylesheet + '</style><div class="frame" part="frame"> <img part="image" alt="" draggable="false" style="display:none"> <div class="empty" part="empty">' + icon + ' <div class="cap"></div> <div class="sub">or <u>browse files</u></div></div> <div class="attr-error" part="attribution-error">' + warnIcon + ' <div class="cap">This photo needs attribution</div></div> <div class="loading" part="loading"></div> <div class="ring" part="ring"></div></div><span class="credit" part="credit"></span><div class="spill" popover="manual" data-dc-edit-transparent> <img class="ghost" alt="" draggable="false"> <div class="handle" data-c="nw"></div><div class="handle" data-c="ne"></div> <div class="handle" data-c="sw"></div><div class="handle" data-c="se"></div></div><div class="ctl" popover="manual" data-dc-edit-transparent><button data-act="replace" title="Replace image">Replace</button> <button data-act="edit" title="Reframe image">Edit</button></div><input type="file" accept="' + ACCEPT.join(",") + '" hidden>';
|
|
4193
|
-
this._frame = root.querySelector(".frame");
|
|
4194
|
-
this._ring = root.querySelector(".ring");
|
|
4195
|
-
this._img = root.querySelector(".frame img");
|
|
4196
|
-
this._empty = root.querySelector(".empty");
|
|
4197
|
-
this._cap = root.querySelector(".cap");
|
|
4198
|
-
this._sub = root.querySelector(".sub");
|
|
4199
|
-
this._spill = root.querySelector(".spill");
|
|
4200
|
-
this._ctl = root.querySelector(".ctl");
|
|
4201
|
-
this._credit = root.querySelector(".credit");
|
|
4202
|
-
this._attrError = root.querySelector(".attr-error");
|
|
4203
|
-
this._credit.addEventListener("click", (e) => e.stopPropagation());
|
|
4204
|
-
this._credit.addEventListener("dblclick", (e) => e.stopPropagation());
|
|
4205
|
-
this._ghost = root.querySelector(".ghost");
|
|
4206
|
-
this._err = null;
|
|
4207
|
-
this._input = root.querySelector("input");
|
|
4208
|
-
this._depth = 0;
|
|
4209
|
-
this._gen = 0;
|
|
4210
|
-
this._swapGen = 0;
|
|
4211
|
-
this._loadPending = false;
|
|
4212
|
-
this._hidShowing = false;
|
|
4213
|
-
this._view = { s: 1, x: 0, y: 0 };
|
|
4214
|
-
this._subFn = () => this._render();
|
|
4215
|
-
this._empty.addEventListener("click", () => this._input.click());
|
|
4216
|
-
root.addEventListener("click", (e) => {
|
|
4217
|
-
const act = e.target && e.target.getAttribute && e.target.getAttribute("data-act");
|
|
4218
|
-
if (!act) return;
|
|
4219
|
-
if (!this.hasAttribute("data-editable")) return;
|
|
4220
|
-
if (act === "replace") {
|
|
4221
|
-
this._exitReframe(true);
|
|
4222
|
-
this.dispatchEvent(new CustomEvent("image-slot:pick", {
|
|
4223
|
-
bubbles: true,
|
|
4224
|
-
composed: true,
|
|
4225
|
-
detail: { id: this.id || null }
|
|
4226
|
-
}));
|
|
4227
|
-
}
|
|
4228
|
-
if (act === "edit") {
|
|
4229
|
-
if (!this._reframes()) return;
|
|
4230
|
-
if (this.hasAttribute("data-reframe")) this._exitReframe(true);
|
|
4231
|
-
else this._enterReframe();
|
|
4232
|
-
}
|
|
4233
|
-
});
|
|
4234
|
-
this._input.addEventListener("change", () => {
|
|
4235
|
-
const f = this._input.files && this._input.files[0];
|
|
4236
|
-
if (f) this._ingest(f);
|
|
4237
|
-
this._input.value = "";
|
|
4238
|
-
});
|
|
4239
|
-
this._img.addEventListener("load", () => {
|
|
4240
|
-
this._loadPending = false;
|
|
4241
|
-
this._releaseMask(true);
|
|
4242
|
-
this._applyView();
|
|
4243
|
-
});
|
|
4244
|
-
this._img.addEventListener("error", () => {
|
|
4245
|
-
this._loadPending = false;
|
|
4246
|
-
this._releaseMask(true);
|
|
4247
|
-
});
|
|
4248
|
-
this.addEventListener("dblclick", (e) => {
|
|
4249
|
-
if (!this.hasAttribute("data-editable") || !this._reframes()) return;
|
|
4250
|
-
e.preventDefault();
|
|
4251
|
-
if (this.hasAttribute("data-reframe")) this._exitReframe(true);
|
|
4252
|
-
else this._enterReframe();
|
|
4253
|
-
});
|
|
4254
|
-
this._spill.addEventListener("pointerdown", (e) => {
|
|
4255
|
-
if (e.button !== 0 || !this.hasAttribute("data-reframe")) return;
|
|
4256
|
-
e.preventDefault();
|
|
4257
|
-
e.stopPropagation();
|
|
4258
|
-
this._spill.setPointerCapture(e.pointerId);
|
|
4259
|
-
const rect = this.getBoundingClientRect();
|
|
4260
|
-
const fw = rect.width || 1, fh = rect.height || 1;
|
|
4261
|
-
const corner = e.target.getAttribute && e.target.getAttribute("data-c");
|
|
4262
|
-
let move;
|
|
4263
|
-
if (corner) {
|
|
4264
|
-
const iw = this._img.naturalWidth || 1, ih = this._img.naturalHeight || 1;
|
|
4265
|
-
const contain = (this.getAttribute("fit") || "cover").toLowerCase() === "contain";
|
|
4266
|
-
const base = contain ? Math.min(fw / iw, fh / ih) : Math.max(fw / iw, fh / ih);
|
|
4267
|
-
const sx = corner.includes("e") ? 1 : -1;
|
|
4268
|
-
const sy = corner.includes("s") ? 1 : -1;
|
|
4269
|
-
const s0 = this._view.s;
|
|
4270
|
-
const w0 = iw * base * s0, h0 = ih * base * s0;
|
|
4271
|
-
const cx0 = (50 + this._view.x) / 100 * fw;
|
|
4272
|
-
const cy0 = (50 + this._view.y) / 100 * fh;
|
|
4273
|
-
const ox = cx0 - sx * w0 / 2, oy = cy0 - sy * h0 / 2;
|
|
4274
|
-
const diag0 = Math.hypot(w0, h0);
|
|
4275
|
-
const ux = sx * w0 / diag0, uy = sy * h0 / diag0;
|
|
4276
|
-
move = (ev) => {
|
|
4277
|
-
const proj = (ev.clientX - rect.left - ox) * ux + (ev.clientY - rect.top - oy) * uy;
|
|
4278
|
-
const s = clampS(s0 * proj / diag0);
|
|
4279
|
-
const d = diag0 * s / s0;
|
|
4280
|
-
this._view.s = s;
|
|
4281
|
-
this._view.x = (ox + ux * d / 2) / fw * 100 - 50;
|
|
4282
|
-
this._view.y = (oy + uy * d / 2) / fh * 100 - 50;
|
|
4283
|
-
this._clampView();
|
|
4284
|
-
this._applyView();
|
|
4285
|
-
};
|
|
4286
|
-
} else {
|
|
4287
|
-
this.setAttribute("data-panning", "");
|
|
4288
|
-
const start = { px: e.clientX, py: e.clientY, x: this._view.x, y: this._view.y };
|
|
4289
|
-
move = (ev) => {
|
|
4290
|
-
this._view.x = start.x + (ev.clientX - start.px) / fw * 100;
|
|
4291
|
-
this._view.y = start.y + (ev.clientY - start.py) / fh * 100;
|
|
4292
|
-
this._clampView();
|
|
4293
|
-
this._applyView();
|
|
4294
|
-
};
|
|
4295
|
-
}
|
|
4296
|
-
const up = () => {
|
|
4297
|
-
try {
|
|
4298
|
-
this._spill.releasePointerCapture(e.pointerId);
|
|
4299
|
-
} catch {
|
|
4300
|
-
}
|
|
4301
|
-
this._spill.removeEventListener("pointermove", move);
|
|
4302
|
-
this._spill.removeEventListener("pointerup", up);
|
|
4303
|
-
this._spill.removeEventListener("pointercancel", up);
|
|
4304
|
-
this.removeAttribute("data-panning");
|
|
4305
|
-
this._dragUp = null;
|
|
4306
|
-
};
|
|
4307
|
-
this._dragUp = up;
|
|
4308
|
-
this._spill.addEventListener("pointermove", move);
|
|
4309
|
-
this._spill.addEventListener("pointerup", up);
|
|
4310
|
-
this._spill.addEventListener("pointercancel", up);
|
|
4311
|
-
});
|
|
4312
|
-
this.addEventListener("wheel", (e) => {
|
|
4313
|
-
if (!this.hasAttribute("data-reframe")) return;
|
|
4314
|
-
e.preventDefault();
|
|
4315
|
-
const r = this.getBoundingClientRect();
|
|
4316
|
-
const cx = (e.clientX - r.left) / r.width * 100 - 50;
|
|
4317
|
-
const cy = (e.clientY - r.top) / r.height * 100 - 50;
|
|
4318
|
-
const prev = this._view.s;
|
|
4319
|
-
const next = clampS(prev * Math.pow(1.0015, -e.deltaY));
|
|
4320
|
-
if (next === prev) return;
|
|
4321
|
-
const k = next / prev;
|
|
4322
|
-
this._view.s = next;
|
|
4323
|
-
this._view.x = cx * (1 - k) + this._view.x * k;
|
|
4324
|
-
this._view.y = cy * (1 - k) + this._view.y * k;
|
|
4325
|
-
this._clampView();
|
|
4326
|
-
this._applyView();
|
|
4327
|
-
}, { passive: false });
|
|
4328
|
-
}
|
|
4329
|
-
connectedCallback() {
|
|
4330
|
-
if (!this.id && !ImageSlot._warned) {
|
|
4331
|
-
ImageSlot._warned = true;
|
|
4332
|
-
console.warn("<image-slot> without an id will not persist its dropped image.");
|
|
4333
|
-
}
|
|
4334
|
-
this.addEventListener("dragenter", this);
|
|
4335
|
-
this.addEventListener("dragover", this);
|
|
4336
|
-
this.addEventListener("dragleave", this);
|
|
4337
|
-
this.addEventListener("drop", this);
|
|
4338
|
-
subs.add(this._subFn);
|
|
4339
|
-
this.addEventListener("pointerenter", this._subFn);
|
|
4340
|
-
this._ro = new ResizeObserver(() => this._render());
|
|
4341
|
-
this._ro.observe(this);
|
|
4342
|
-
load();
|
|
4343
|
-
this._render();
|
|
4344
|
-
}
|
|
4345
|
-
disconnectedCallback() {
|
|
4346
|
-
subs.delete(this._subFn);
|
|
4347
|
-
this.removeEventListener("pointerenter", this._subFn);
|
|
4348
|
-
this.removeEventListener("dragenter", this);
|
|
4349
|
-
this.removeEventListener("dragover", this);
|
|
4350
|
-
this.removeEventListener("dragleave", this);
|
|
4351
|
-
this.removeEventListener("drop", this);
|
|
4352
|
-
if (this._ro) {
|
|
4353
|
-
this._ro.disconnect();
|
|
4354
|
-
this._ro = null;
|
|
4355
|
-
}
|
|
4356
|
-
this._exitReframe(false);
|
|
4357
|
-
}
|
|
4358
|
-
_enterReframe() {
|
|
4359
|
-
if (this.hasAttribute("data-reframe")) return;
|
|
4360
|
-
this.setAttribute("data-reframe", "");
|
|
4361
|
-
this._signalReframe(true);
|
|
4362
|
-
this._pagehide = () => {
|
|
4363
|
-
this._exitReframe(true);
|
|
4364
|
-
flushNow();
|
|
4365
|
-
};
|
|
4366
|
-
window.addEventListener("pagehide", this._pagehide);
|
|
4367
|
-
try {
|
|
4368
|
-
this._spill.showPopover();
|
|
4369
|
-
} catch {
|
|
4370
|
-
}
|
|
4371
|
-
try {
|
|
4372
|
-
this._ctl.showPopover();
|
|
4373
|
-
} catch {
|
|
4374
|
-
}
|
|
4375
|
-
this._reposition = () => {
|
|
4376
|
-
if (this.hasAttribute("data-reframe")) this._applyView();
|
|
4377
|
-
};
|
|
4378
|
-
window.addEventListener("scroll", this._reposition, true);
|
|
4379
|
-
window.addEventListener("resize", this._reposition);
|
|
4380
|
-
this._lastRect = "";
|
|
4381
|
-
this._watch = () => {
|
|
4382
|
-
if (!this.hasAttribute("data-reframe")) return;
|
|
4383
|
-
const r = this.getBoundingClientRect();
|
|
4384
|
-
const key = r.left + "," + r.top + "," + r.width + "," + r.height;
|
|
4385
|
-
if (key !== this._lastRect) {
|
|
4386
|
-
this._lastRect = key;
|
|
4387
|
-
this._applyView();
|
|
4388
|
-
}
|
|
4389
|
-
this._watchId = requestAnimationFrame(this._watch);
|
|
4390
|
-
};
|
|
4391
|
-
this._watchId = requestAnimationFrame(this._watch);
|
|
4392
|
-
this._applyView();
|
|
4393
|
-
this._outside = (e) => {
|
|
4394
|
-
if (e.composedPath && e.composedPath().includes(this)) return;
|
|
4395
|
-
this._exitReframe(true);
|
|
4396
|
-
};
|
|
4397
|
-
this._esc = (e) => {
|
|
4398
|
-
if (e.key === "Escape") this._exitReframe(true);
|
|
4399
|
-
};
|
|
4400
|
-
document.addEventListener("pointerdown", this._outside, true);
|
|
4401
|
-
document.addEventListener("keydown", this._esc, true);
|
|
4402
|
-
}
|
|
4403
|
-
_exitReframe(commit) {
|
|
4404
|
-
if (!this.hasAttribute("data-reframe")) return;
|
|
4405
|
-
if (this._dragUp) this._dragUp();
|
|
4406
|
-
this.removeAttribute("data-reframe");
|
|
4407
|
-
this.removeAttribute("data-panning");
|
|
4408
|
-
if (this._outside) document.removeEventListener("pointerdown", this._outside, true);
|
|
4409
|
-
if (this._esc) document.removeEventListener("keydown", this._esc, true);
|
|
4410
|
-
this._outside = this._esc = null;
|
|
4411
|
-
if (this._reposition) {
|
|
4412
|
-
window.removeEventListener("scroll", this._reposition, true);
|
|
4413
|
-
window.removeEventListener("resize", this._reposition);
|
|
4414
|
-
this._reposition = null;
|
|
4415
|
-
}
|
|
4416
|
-
if (this._watchId) {
|
|
4417
|
-
cancelAnimationFrame(this._watchId);
|
|
4418
|
-
this._watchId = 0;
|
|
4419
|
-
}
|
|
4420
|
-
if (this._pagehide) {
|
|
4421
|
-
window.removeEventListener("pagehide", this._pagehide);
|
|
4422
|
-
this._pagehide = null;
|
|
4423
|
-
}
|
|
4424
|
-
try {
|
|
4425
|
-
this._spill.hidePopover();
|
|
4426
|
-
} catch {
|
|
4427
|
-
}
|
|
4428
|
-
try {
|
|
4429
|
-
this._ctl.hidePopover();
|
|
4430
|
-
} catch {
|
|
4431
|
-
}
|
|
4432
|
-
this._ctl.style.left = "";
|
|
4433
|
-
this._ctl.style.top = "";
|
|
4434
|
-
if (commit) this._commitView();
|
|
4435
|
-
this._signalReframe(false);
|
|
4436
|
-
}
|
|
4437
|
-
// Reframe state lives only in this DOM until commit, invisible to the
|
|
4438
|
-
// host's dirty signals — announce enter/exit so the host can hold
|
|
4439
|
-
// auto-reloads for exactly the gesture (the guest bundle forwards
|
|
4440
|
-
// image-slot:reframe to the host as imageSlotReframe). Dispatched on
|
|
4441
|
-
// the element (composed, so it escapes shadow roots) while connected;
|
|
4442
|
-
// a disconnected exit (disconnectedCallback) falls back to document so
|
|
4443
|
-
// the host still hears it.
|
|
4444
|
-
_signalReframe(active) {
|
|
4445
|
-
const target = this.isConnected ? this : document;
|
|
4446
|
-
target.dispatchEvent(new CustomEvent("image-slot:reframe", {
|
|
4447
|
-
bubbles: true,
|
|
4448
|
-
composed: true,
|
|
4449
|
-
detail: { active, id: this.id || null }
|
|
4450
|
-
}));
|
|
4451
|
-
}
|
|
4452
|
-
// Public: host's "Import from computer" calls this to run local browse.
|
|
4453
|
-
openFilePicker() {
|
|
4454
|
-
this._exitReframe(true);
|
|
4455
|
-
this._input.click();
|
|
4456
|
-
}
|
|
4457
|
-
attributeChangedCallback() {
|
|
4458
|
-
if (this.shadowRoot) this._render();
|
|
4459
|
-
}
|
|
4460
|
-
// handleEvent — one listener object for all four drag events keeps the
|
|
4461
|
-
// add/remove symmetric and the depth counter correct.
|
|
4462
|
-
handleEvent(e) {
|
|
4463
|
-
if (e.type === "dragenter" || e.type === "dragover") {
|
|
4464
|
-
e.preventDefault();
|
|
4465
|
-
e.stopPropagation();
|
|
4466
|
-
if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
|
|
4467
|
-
if (e.type === "dragenter") this._depth++;
|
|
4468
|
-
this.setAttribute("data-over", "");
|
|
4469
|
-
} else if (e.type === "dragleave") {
|
|
4470
|
-
if (--this._depth <= 0) {
|
|
4471
|
-
this._depth = 0;
|
|
4472
|
-
this.removeAttribute("data-over");
|
|
4473
|
-
}
|
|
4474
|
-
} else if (e.type === "drop") {
|
|
4475
|
-
e.preventDefault();
|
|
4476
|
-
e.stopPropagation();
|
|
4477
|
-
this._depth = 0;
|
|
4478
|
-
this.removeAttribute("data-over");
|
|
4479
|
-
const f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];
|
|
4480
|
-
if (f) this._ingest(f);
|
|
4481
|
-
}
|
|
4482
|
-
}
|
|
4483
|
-
async _ingest(file) {
|
|
4484
|
-
this._setError(null);
|
|
4485
|
-
if (!file || ACCEPT.indexOf(file.type) < 0) {
|
|
4486
|
-
this._setError("Drop a PNG, JPEG, WebP, or AVIF image.");
|
|
4487
|
-
return;
|
|
4488
|
-
}
|
|
4489
|
-
const gen = ++this._gen;
|
|
4490
|
-
if (this.hasAttribute("data-filled")) {
|
|
4491
|
-
this.setAttribute("data-swapping", "");
|
|
4492
|
-
this._swapGen = gen;
|
|
4493
|
-
}
|
|
4494
|
-
try {
|
|
4495
|
-
const w = this.clientWidth || this.offsetWidth || MAX_DIM;
|
|
4496
|
-
const url = await toDataUrl(file, w);
|
|
4497
|
-
if (gen !== this._gen) return;
|
|
4498
|
-
this._exitReframe(false);
|
|
4499
|
-
this._swapGen = 0;
|
|
4500
|
-
const val = { u: url, s: 1, x: 0, y: 0 };
|
|
4501
|
-
setSlot(this.id || "", val);
|
|
4502
|
-
if (!this.id) {
|
|
4503
|
-
this._local = val;
|
|
4504
|
-
this._render();
|
|
4505
|
-
}
|
|
4506
|
-
} catch (err) {
|
|
4507
|
-
if (gen !== this._gen) return;
|
|
4508
|
-
this._swapGen = 0;
|
|
4509
|
-
this._releaseMask();
|
|
4510
|
-
this._setError("Could not read that image.");
|
|
4511
|
-
console.warn("<image-slot> ingest failed:", err);
|
|
4512
|
-
}
|
|
4513
|
-
}
|
|
4514
|
-
_setError(msg) {
|
|
4515
|
-
if (this._err) {
|
|
4516
|
-
this._err.remove();
|
|
4517
|
-
this._err = null;
|
|
4518
|
-
}
|
|
4519
|
-
if (!msg) return;
|
|
4520
|
-
const d = document.createElement("div");
|
|
4521
|
-
d.className = "err";
|
|
4522
|
-
d.textContent = msg;
|
|
4523
|
-
this.shadowRoot.appendChild(d);
|
|
4524
|
-
this._err = d;
|
|
4525
|
-
setTimeout(() => {
|
|
4526
|
-
if (this._err === d) {
|
|
4527
|
-
d.remove();
|
|
4528
|
-
this._err = null;
|
|
4529
|
-
}
|
|
4530
|
-
}, 3e3);
|
|
4531
|
-
}
|
|
4532
|
-
// Reframing (pan/resize) is available on any filled slot — the user can
|
|
4533
|
-
// always reposition/scale. `fit` only sets the initial baseline (see
|
|
4534
|
-
// _geom): contain starts fully-visible, cover starts frame-filling.
|
|
4535
|
-
_reframes() {
|
|
4536
|
-
return this.hasAttribute("data-filled");
|
|
4537
|
-
}
|
|
4538
|
-
// The single release discipline for the replacement-in-flight mask
|
|
4539
|
-
// (data-swapping). The mask comes off only when BOTH hold:
|
|
4540
|
-
// - no encode is pending (_swapGen) — mid-encode the stored value
|
|
4541
|
-
// still resolves to the old image, so any reveal paints it;
|
|
4542
|
-
// - the frame img has settled on its current src — an unsettled src
|
|
4543
|
-
// means some replacement is still in flight (e.g. a remote pick),
|
|
4544
|
-
// whoever started it, and revealing would paint the previous
|
|
4545
|
-
// frame. The load/error listeners pass settled=true (the event IS
|
|
4546
|
-
// the settlement signal, per spec complete is true by then);
|
|
4547
|
-
// other callers rely on the complete flag (covers loaded AND
|
|
4548
|
-
// failed).
|
|
4549
|
-
// Every release path funnels through here EXCEPT _render's empty
|
|
4550
|
-
// branch (the img is being cleared — nothing will ever settle).
|
|
4551
|
-
_releaseMask(settled) {
|
|
4552
|
-
if (!this._swapGen && !this._loadPending && (settled || this._img.complete)) {
|
|
4553
|
-
this.removeAttribute("data-swapping");
|
|
4554
|
-
}
|
|
4555
|
-
}
|
|
4556
|
-
// Baseline geometry, shared by clamp/apply/resize. `base` is the scale at
|
|
4557
|
-
// view-scale s=1: cover = fill the frame (overflow on the looser axis),
|
|
4558
|
-
// contain = fit fully inside (letterboxed). Zooming a contain image past
|
|
4559
|
-
// s where it overflows naturally becomes a crop. Null until the img has
|
|
4560
|
-
// loaded (naturalWidth is 0 before that) or when the slot has no layout
|
|
4561
|
-
// box — ResizeObserver fires with a 0×0 rect under display:none, and
|
|
4562
|
-
// clamping against a degenerate 1×1 frame would silently pull the stored
|
|
4563
|
-
// pan toward zero.
|
|
4564
|
-
_geom() {
|
|
4565
|
-
const iw = this._img.naturalWidth, ih = this._img.naturalHeight;
|
|
4566
|
-
const fw = this.clientWidth, fh = this.clientHeight;
|
|
4567
|
-
if (!iw || !ih || !fw || !fh) return null;
|
|
4568
|
-
const contain = (this.getAttribute("fit") || "cover").toLowerCase() === "contain";
|
|
4569
|
-
const base = contain ? Math.min(fw / iw, fh / ih) : Math.max(fw / iw, fh / ih);
|
|
4570
|
-
return { iw, ih, fw, fh, base };
|
|
4571
|
-
}
|
|
4572
|
-
_clampView() {
|
|
4573
|
-
const g = this._geom();
|
|
4574
|
-
if (!g) return;
|
|
4575
|
-
const mx = Math.max(0, (g.iw * g.base * this._view.s / g.fw - 1) * 50);
|
|
4576
|
-
const my = Math.max(0, (g.ih * g.base * this._view.s / g.fh - 1) * 50);
|
|
4577
|
-
this._view.x = Math.max(-mx, Math.min(mx, this._view.x));
|
|
4578
|
-
this._view.y = Math.max(-my, Math.min(my, this._view.y));
|
|
4579
|
-
}
|
|
4580
|
-
_applyView() {
|
|
4581
|
-
const g = this._geom();
|
|
4582
|
-
if (this.hasAttribute("data-reframe")) {
|
|
4583
|
-
let onTop = false;
|
|
4584
|
-
try {
|
|
4585
|
-
onTop = this._ctl.matches(":popover-open");
|
|
4586
|
-
} catch {
|
|
4587
|
-
}
|
|
4588
|
-
if (onTop) {
|
|
4589
|
-
const r = this.getBoundingClientRect();
|
|
4590
|
-
this._ctl.style.left = r.right - 8 + "px";
|
|
4591
|
-
this._ctl.style.top = r.top + 8 + "px";
|
|
4592
|
-
}
|
|
4593
|
-
}
|
|
4594
|
-
if (!g) {
|
|
4595
|
-
const contain = (this.getAttribute("fit") || "cover").toLowerCase() === "contain";
|
|
4596
|
-
this._img.style.width = "100%";
|
|
4597
|
-
this._img.style.height = "100%";
|
|
4598
|
-
this._img.style.left = "50%";
|
|
4599
|
-
this._img.style.top = "50%";
|
|
4600
|
-
this._img.style.objectFit = contain ? "contain" : "cover";
|
|
4601
|
-
return;
|
|
4602
|
-
}
|
|
4603
|
-
const k = g.base * this._view.s;
|
|
4604
|
-
const w = g.iw * k / g.fw * 100 + "%";
|
|
4605
|
-
const h = g.ih * k / g.fh * 100 + "%";
|
|
4606
|
-
const l = 50 + this._view.x + "%";
|
|
4607
|
-
const t = 50 + this._view.y + "%";
|
|
4608
|
-
this._img.style.width = w;
|
|
4609
|
-
this._img.style.height = h;
|
|
4610
|
-
this._img.style.left = l;
|
|
4611
|
-
this._img.style.top = t;
|
|
4612
|
-
this._img.style.objectFit = "";
|
|
4613
|
-
if (this.hasAttribute("data-reframe")) {
|
|
4614
|
-
const r = this.getBoundingClientRect();
|
|
4615
|
-
const sx = g.fw ? r.width / g.fw : 1;
|
|
4616
|
-
const sy = g.fh ? r.height / g.fh : 1;
|
|
4617
|
-
this._spill.style.width = g.iw * k * sx + "px";
|
|
4618
|
-
this._spill.style.height = g.ih * k * sy + "px";
|
|
4619
|
-
this._spill.style.left = r.left + (50 + this._view.x) / 100 * r.width + "px";
|
|
4620
|
-
this._spill.style.top = r.top + (50 + this._view.y) / 100 * r.height + "px";
|
|
4621
|
-
}
|
|
4622
|
-
}
|
|
4623
|
-
_commitView() {
|
|
4624
|
-
const v = { s: this._view.s, x: this._view.x, y: this._view.y };
|
|
4625
|
-
if (this._userUrl) v.u = this._userUrl;
|
|
4626
|
-
if (this.id) setSlot(this.id, v);
|
|
4627
|
-
else {
|
|
4628
|
-
this._local = v;
|
|
4629
|
-
}
|
|
4630
|
-
}
|
|
4631
|
-
_render() {
|
|
4632
|
-
const mask = this.getAttribute("mask");
|
|
4633
|
-
const shape = (this.getAttribute("shape") || "rounded").toLowerCase();
|
|
4634
|
-
let radius = "";
|
|
4635
|
-
if (shape === "circle") radius = "50%";
|
|
4636
|
-
else if (shape === "pill") radius = "9999px";
|
|
4637
|
-
else if (shape === "rounded") {
|
|
4638
|
-
const n = parseFloat(this.getAttribute("radius"));
|
|
4639
|
-
radius = (Number.isFinite(n) ? n : 12) + "px";
|
|
4640
|
-
}
|
|
4641
|
-
this._frame.style.borderRadius = mask ? "" : radius;
|
|
4642
|
-
this._frame.style.clipPath = mask || "";
|
|
4643
|
-
this._ring.style.borderRadius = mask ? "" : radius;
|
|
4644
|
-
this._ring.style.display = mask ? "none" : "";
|
|
4645
|
-
const editable = !!(window.omelette && window.omelette.writeFile);
|
|
4646
|
-
this.toggleAttribute("data-editable", editable);
|
|
4647
|
-
this._sub.style.display = editable ? "" : "none";
|
|
4648
|
-
let stored = this.id ? getSlot(this.id) : this._local;
|
|
4649
|
-
if (stored && stored.u && !/^data:image\//i.test(stored.u)) stored = null;
|
|
4650
|
-
const srcAttr = this.getAttribute("src") || "";
|
|
4651
|
-
this._userUrl = stored && stored.u || null;
|
|
4652
|
-
const url = this._userUrl || srcAttr;
|
|
4653
|
-
if (!this.hasAttribute("data-reframe")) {
|
|
4654
|
-
this._view = {
|
|
4655
|
-
s: stored && Number.isFinite(stored.s) ? clampS(stored.s) : 1,
|
|
4656
|
-
x: stored && Number.isFinite(stored.x) ? stored.x : 0,
|
|
4657
|
-
y: stored && Number.isFinite(stored.y) ? stored.y : 0
|
|
4658
|
-
};
|
|
4659
|
-
}
|
|
4660
|
-
this._cap.textContent = this.getAttribute("placeholder") || "Drop an image";
|
|
4661
|
-
const credit = (this.getAttribute("credit") || "").trim();
|
|
4662
|
-
const attrError = !!(!credit && !this._userUrl && srcAttr && isUnsplashHost(srcAttr));
|
|
4663
|
-
this.toggleAttribute("data-attribution-error", attrError);
|
|
4664
|
-
if (url && !attrError) {
|
|
4665
|
-
const prev = this._img.getAttribute("src");
|
|
4666
|
-
if (prev !== url) {
|
|
4667
|
-
if (prev || this._hidShowing) this.setAttribute("data-swapping", "");
|
|
4668
|
-
this._loadPending = true;
|
|
4669
|
-
this._img.src = url;
|
|
4670
|
-
this._ghost.src = url;
|
|
4671
|
-
} else {
|
|
4672
|
-
this._releaseMask();
|
|
4673
|
-
}
|
|
4674
|
-
this._hidShowing = false;
|
|
4675
|
-
this._img.style.display = "block";
|
|
4676
|
-
this._empty.style.display = "none";
|
|
4677
|
-
this.setAttribute("data-filled", "");
|
|
4678
|
-
this._clampView();
|
|
4679
|
-
this._applyView();
|
|
4680
|
-
} else {
|
|
4681
|
-
this.removeAttribute("data-swapping");
|
|
4682
|
-
this._loadPending = false;
|
|
4683
|
-
this._hidShowing = attrError && !!this._img.getAttribute("src");
|
|
4684
|
-
this._img.style.display = "none";
|
|
4685
|
-
this._img.removeAttribute("src");
|
|
4686
|
-
this._ghost.removeAttribute("src");
|
|
4687
|
-
this._empty.style.display = attrError ? "none" : "flex";
|
|
4688
|
-
this.removeAttribute("data-filled");
|
|
4689
|
-
}
|
|
4690
|
-
const showCredit = !!(url && credit && !this._userUrl && !attrError);
|
|
4691
|
-
this._credit.textContent = "";
|
|
4692
|
-
if (showCredit) {
|
|
4693
|
-
let href = "";
|
|
4694
|
-
const rawHref = this.getAttribute("credit-href") || "";
|
|
4695
|
-
if (rawHref) {
|
|
4696
|
-
try {
|
|
4697
|
-
const u = new URL(rawHref, document.baseURI);
|
|
4698
|
-
if (u.protocol === "http:" || u.protocol === "https:") {
|
|
4699
|
-
href = withReferral(u.href);
|
|
4700
|
-
}
|
|
4701
|
-
} catch {
|
|
4702
|
-
}
|
|
4703
|
-
}
|
|
4704
|
-
const mkLink = (text, linkHref) => {
|
|
4705
|
-
const a = document.createElement("a");
|
|
4706
|
-
a.setAttribute("target", "_blank");
|
|
4707
|
-
a.setAttribute("rel", "noopener noreferrer");
|
|
4708
|
-
a.setAttribute("href", linkHref);
|
|
4709
|
-
a.textContent = text;
|
|
4710
|
-
return a;
|
|
4711
|
-
};
|
|
4712
|
-
const m = /^Photo by (.+) on Unsplash$/.exec(credit);
|
|
4713
|
-
if (m) {
|
|
4714
|
-
this._credit.appendChild(document.createTextNode("Photo by "));
|
|
4715
|
-
this._credit.appendChild(
|
|
4716
|
-
href ? mkLink(m[1], href) : document.createTextNode(m[1])
|
|
4717
|
-
);
|
|
4718
|
-
this._credit.appendChild(document.createTextNode(" on "));
|
|
4719
|
-
this._credit.appendChild(mkLink("Unsplash", UNSPLASH_HOMEPAGE_HREF));
|
|
4720
|
-
} else if (href) {
|
|
4721
|
-
this._credit.appendChild(mkLink(credit, href));
|
|
4722
|
-
} else {
|
|
4723
|
-
this._credit.textContent = credit;
|
|
4724
|
-
}
|
|
4725
|
-
}
|
|
4726
|
-
this.toggleAttribute("data-credit", showCredit);
|
|
4727
|
-
}
|
|
4728
|
-
}
|
|
4729
|
-
if (!customElements.get("image-slot")) {
|
|
4730
|
-
customElements.define("image-slot", ImageSlot);
|
|
4731
|
-
}
|
|
4732
|
-
})();
|
|
4733
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "image-slot.js", error: String((e && e.message) || e) }); }
|
|
4734
|
-
|
|
4735
4872
|
// tokens/tokens.js
|
|
4736
4873
|
try { (() => {
|
|
4737
4874
|
const tokens = {
|
|
4738
4875
|
"$meta": {
|
|
4739
4876
|
"system": "CyberSkill Design System",
|
|
4740
|
-
"version": "1.
|
|
4877
|
+
"version": "1.3.8",
|
|
4741
4878
|
"generated": "2026-07-23",
|
|
4742
4879
|
"note": "Generated from tokens/*.css. Root grouped by category; theme/element scopes carry overrides only. Regenerate after any token change (see docs/conventions.md)."
|
|
4743
4880
|
},
|
|
@@ -4756,6 +4893,10 @@ const tokens = {
|
|
|
4756
4893
|
"--cs-color-semantic-danger": "#B42318",
|
|
4757
4894
|
"--cs-color-semantic-warning": "#92400E",
|
|
4758
4895
|
"--cs-color-semantic-info": "#1D4ED8",
|
|
4896
|
+
"--cs-color-semantic-success-fg": "#FFFFFF",
|
|
4897
|
+
"--cs-color-semantic-danger-fg": "#FFFFFF",
|
|
4898
|
+
"--cs-color-semantic-warning-fg": "#FFFFFF",
|
|
4899
|
+
"--cs-color-semantic-info-fg": "#FFFFFF",
|
|
4759
4900
|
"--cs-color-accent-ochre": "#F4BA17",
|
|
4760
4901
|
"--cs-color-border-default": "#E7D9C6",
|
|
4761
4902
|
"--cs-color-link": "#6E3B0E",
|
|
@@ -4785,7 +4926,8 @@ const tokens = {
|
|
|
4785
4926
|
"--cs-component-textfield-border-default": "#BFA58F",
|
|
4786
4927
|
"--cs-component-textfield-paddingY": "10px",
|
|
4787
4928
|
"--cs-component-textfield-paddingX": "12px",
|
|
4788
|
-
"--cs-component-textfield-minHeight": "44px"
|
|
4929
|
+
"--cs-component-textfield-minHeight": "44px",
|
|
4930
|
+
"--cs-focus-ring": "3px"
|
|
4789
4931
|
},
|
|
4790
4932
|
"accent": {
|
|
4791
4933
|
"--cs-accent": "#F4BA17",
|
|
@@ -4796,7 +4938,8 @@ const tokens = {
|
|
|
4796
4938
|
"--cs-accent-ink": "#45210E",
|
|
4797
4939
|
"--cs-accent-glow": "rgba(244, 186, 23, 0.48)",
|
|
4798
4940
|
"--cs-accent-grad-a": "#F4BA17",
|
|
4799
|
-
"--cs-accent-grad-b": "#
|
|
4941
|
+
"--cs-accent-grad-b": "#F3B958",
|
|
4942
|
+
"--cs-accent-on-strong": "#1A1108"
|
|
4800
4943
|
},
|
|
4801
4944
|
"depth": {
|
|
4802
4945
|
"--cs-depth-bg": "0",
|
|
@@ -4804,7 +4947,9 @@ const tokens = {
|
|
|
4804
4947
|
"--cs-depth-card": "10",
|
|
4805
4948
|
"--cs-depth-nav": "50",
|
|
4806
4949
|
"--cs-depth-modal": "100",
|
|
4807
|
-
"--cs-depth-toast": "200"
|
|
4950
|
+
"--cs-depth-toast": "200",
|
|
4951
|
+
"--cs-depth-dropdown": "60",
|
|
4952
|
+
"--cs-depth-overlay": "80"
|
|
4808
4953
|
},
|
|
4809
4954
|
"glass": {
|
|
4810
4955
|
"--cs-glass-whisper-blur": "8px",
|
|
@@ -4862,6 +5007,7 @@ const tokens = {
|
|
|
4862
5007
|
},
|
|
4863
5008
|
"breakpoint": {
|
|
4864
5009
|
"--cs-breakpoint-xs": "0px",
|
|
5010
|
+
"--cs-breakpoint-phone": "360px",
|
|
4865
5011
|
"--cs-breakpoint-sm": "640px",
|
|
4866
5012
|
"--cs-breakpoint-md": "768px",
|
|
4867
5013
|
"--cs-breakpoint-lg": "1024px",
|
|
@@ -4869,7 +5015,8 @@ const tokens = {
|
|
|
4869
5015
|
"--cs-breakpoint-2xl": "1536px"
|
|
4870
5016
|
},
|
|
4871
5017
|
"container": {
|
|
4872
|
-
"--cs-container-max": "1320px"
|
|
5018
|
+
"--cs-container-max": "1320px",
|
|
5019
|
+
"--cs-prose-measure": "45rem"
|
|
4873
5020
|
},
|
|
4874
5021
|
"font": {
|
|
4875
5022
|
"--cs-font-family-ui": "'Be Vietnam Pro', ui-sans-serif, system-ui, sans-serif",
|
|
@@ -4883,6 +5030,7 @@ const tokens = {
|
|
|
4883
5030
|
"--cs-font-lineHeight-body": "1.5",
|
|
4884
5031
|
"--cs-font-lineHeight-heading": "1.35",
|
|
4885
5032
|
"--cs-font-lineHeight-tight": "1.15",
|
|
5033
|
+
"--cs-font-size-2xs": "0.6875rem",
|
|
4886
5034
|
"--cs-font-size-xs": "0.75rem",
|
|
4887
5035
|
"--cs-font-size-sm": "0.875rem",
|
|
4888
5036
|
"--cs-font-size-base": "1rem",
|
|
@@ -4903,6 +5051,7 @@ const tokens = {
|
|
|
4903
5051
|
"--cs-text-body-lg": "var(--cs-font-size-md)",
|
|
4904
5052
|
"--cs-text-body": "var(--cs-font-size-base)",
|
|
4905
5053
|
"--cs-text-secondary": "var(--cs-font-size-sm)",
|
|
5054
|
+
"--cs-text-micro": "var(--cs-font-size-2xs)",
|
|
4906
5055
|
"--cs-text-caption": "var(--cs-font-size-xs)",
|
|
4907
5056
|
"--cs-text-eyebrow": "var(--cs-font-size-xs)"
|
|
4908
5057
|
},
|
|
@@ -4921,6 +5070,12 @@ const tokens = {
|
|
|
4921
5070
|
"--cs-icon-sm": "16px",
|
|
4922
5071
|
"--cs-icon-md": "20px",
|
|
4923
5072
|
"--cs-icon-lg": "24px"
|
|
5073
|
+
},
|
|
5074
|
+
"doc": {
|
|
5075
|
+
"--cs-doc-ink": "#1A1614",
|
|
5076
|
+
"--cs-doc-slate": "#3F4C55",
|
|
5077
|
+
"--cs-doc-fill": "#FDE68A",
|
|
5078
|
+
"--cs-doc-fill-ink": "#1A1614"
|
|
4924
5079
|
}
|
|
4925
5080
|
},
|
|
4926
5081
|
"themes": {
|
|
@@ -4936,6 +5091,10 @@ const tokens = {
|
|
|
4936
5091
|
"--cs-color-semantic-danger": "#F87171",
|
|
4937
5092
|
"--cs-color-semantic-warning": "#FBBF24",
|
|
4938
5093
|
"--cs-color-semantic-info": "#7CB2FB",
|
|
5094
|
+
"--cs-color-semantic-success-fg": "#1a1108",
|
|
5095
|
+
"--cs-color-semantic-danger-fg": "#1a1108",
|
|
5096
|
+
"--cs-color-semantic-warning-fg": "#1a1108",
|
|
5097
|
+
"--cs-color-semantic-info-fg": "#1a1108",
|
|
4939
5098
|
"--cs-color-border-default": "#4a3a2c",
|
|
4940
5099
|
"--cs-color-link": "#f4ba17",
|
|
4941
5100
|
"--cs-color-link-hover": "#f5ead9",
|
|
@@ -4960,6 +5119,10 @@ const tokens = {
|
|
|
4960
5119
|
"--cs-color-semantic-danger": "#F87171",
|
|
4961
5120
|
"--cs-color-semantic-warning": "#FBBF24",
|
|
4962
5121
|
"--cs-color-semantic-info": "#7CB2FB",
|
|
5122
|
+
"--cs-color-semantic-success-fg": "#1a1108",
|
|
5123
|
+
"--cs-color-semantic-danger-fg": "#1a1108",
|
|
5124
|
+
"--cs-color-semantic-warning-fg": "#1a1108",
|
|
5125
|
+
"--cs-color-semantic-info-fg": "#1a1108",
|
|
4963
5126
|
"--cs-color-border-default": "#4a3a2c",
|
|
4964
5127
|
"--cs-color-link": "#f4ba17",
|
|
4965
5128
|
"--cs-color-link-hover": "#f5ead9",
|
|
@@ -4979,35 +5142,38 @@ const tokens = {
|
|
|
4979
5142
|
"--cs-accent-strong": "#90825E",
|
|
4980
5143
|
"--cs-accent-bright": "#F6EBCC",
|
|
4981
5144
|
"--cs-accent-on": "#292414",
|
|
5145
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
4982
5146
|
"--cs-accent-tint": "#FBF7EA",
|
|
4983
5147
|
"--cs-accent-ink": "#292414",
|
|
4984
5148
|
"--cs-accent-glow": "rgba(248, 233, 192, 0.48)",
|
|
4985
5149
|
"--cs-accent-grad-a": "#F8E9C0",
|
|
4986
|
-
"--cs-accent-grad-b": "#
|
|
5150
|
+
"--cs-accent-grad-b": "#F3B958",
|
|
4987
5151
|
"--cs-color-text-accent": "#90825E",
|
|
4988
|
-
"--cs-color-link": "#
|
|
5152
|
+
"--cs-color-link": "#7B6D4A"
|
|
4989
5153
|
},
|
|
4990
5154
|
"tho": {
|
|
4991
5155
|
"--cs-accent": "#F4BA17",
|
|
4992
5156
|
"--cs-accent-strong": "#A47D17",
|
|
4993
5157
|
"--cs-accent-bright": "#F4BA17",
|
|
4994
5158
|
"--cs-accent-on": "#45210E",
|
|
5159
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
4995
5160
|
"--cs-accent-tint": "#FBF4E9",
|
|
4996
5161
|
"--cs-accent-ink": "#45210E",
|
|
4997
5162
|
"--cs-accent-glow": "rgba(244, 186, 23, 0.48)",
|
|
4998
5163
|
"--cs-accent-grad-a": "#F4BA17",
|
|
4999
|
-
"--cs-accent-grad-b": "#
|
|
5164
|
+
"--cs-accent-grad-b": "#F3B958"
|
|
5000
5165
|
},
|
|
5001
5166
|
"tho.clay": {
|
|
5002
5167
|
"--cs-accent": "#513801",
|
|
5003
5168
|
"--cs-accent-strong": "#201300",
|
|
5004
5169
|
"--cs-accent-bright": "#DEC7A2",
|
|
5005
5170
|
"--cs-accent-on": "#2D220E",
|
|
5171
|
+
"--cs-accent-on-strong": "#FFFFFF",
|
|
5006
5172
|
"--cs-accent-tint": "#FFF6E7",
|
|
5007
5173
|
"--cs-accent-ink": "#2D220E",
|
|
5008
5174
|
"--cs-accent-glow": "rgba(81, 56, 1, 0.48)",
|
|
5009
5175
|
"--cs-accent-grad-a": "#513801",
|
|
5010
|
-
"--cs-accent-grad-b": "#
|
|
5176
|
+
"--cs-accent-grad-b": "#F3B958",
|
|
5011
5177
|
"--cs-color-text-accent": "#201300",
|
|
5012
5178
|
"--cs-color-link": "#201300"
|
|
5013
5179
|
},
|
|
@@ -5016,32 +5182,35 @@ const tokens = {
|
|
|
5016
5182
|
"--cs-accent-strong": "#967C75",
|
|
5017
5183
|
"--cs-accent-bright": "#FCE6DF",
|
|
5018
5184
|
"--cs-accent-on": "#2C211E",
|
|
5185
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
5019
5186
|
"--cs-accent-tint": "#FEF4F2",
|
|
5020
5187
|
"--cs-accent-ink": "#2C211E",
|
|
5021
5188
|
"--cs-accent-glow": "rgba(255, 225, 216, 0.48)",
|
|
5022
5189
|
"--cs-accent-grad-a": "#FFE1D8",
|
|
5023
5190
|
"--cs-accent-grad-b": "#F4BA17",
|
|
5024
5191
|
"--cs-color-text-accent": "#967C75",
|
|
5025
|
-
"--cs-color-link": "#
|
|
5192
|
+
"--cs-color-link": "#846B64"
|
|
5026
5193
|
},
|
|
5027
5194
|
"hoa": {
|
|
5028
5195
|
"--cs-accent": "#FCAF91",
|
|
5029
5196
|
"--cs-accent-strong": "#B47054",
|
|
5030
5197
|
"--cs-accent-bright": "#FED3C3",
|
|
5031
5198
|
"--cs-accent-on": "#381B10",
|
|
5199
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
5032
5200
|
"--cs-accent-tint": "#FFF4F0",
|
|
5033
5201
|
"--cs-accent-ink": "#381B10",
|
|
5034
5202
|
"--cs-accent-glow": "rgba(252, 175, 145, 0.48)",
|
|
5035
5203
|
"--cs-accent-grad-a": "#FCAF91",
|
|
5036
5204
|
"--cs-accent-grad-b": "#F4BA17",
|
|
5037
5205
|
"--cs-color-text-accent": "#B47054",
|
|
5038
|
-
"--cs-color-link": "#
|
|
5206
|
+
"--cs-color-link": "#A15E43"
|
|
5039
5207
|
},
|
|
5040
5208
|
"hoa.lava": {
|
|
5041
5209
|
"--cs-accent": "#622B06",
|
|
5042
5210
|
"--cs-accent-strong": "#280D00",
|
|
5043
5211
|
"--cs-accent-bright": "#F1BEA4",
|
|
5044
5212
|
"--cs-accent-on": "#361D0F",
|
|
5213
|
+
"--cs-accent-on-strong": "#FFFFFF",
|
|
5045
5214
|
"--cs-accent-tint": "#FFF4EF",
|
|
5046
5215
|
"--cs-accent-ink": "#361D0F",
|
|
5047
5216
|
"--cs-accent-glow": "rgba(98, 43, 6, 0.48)",
|
|
@@ -5055,32 +5224,35 @@ const tokens = {
|
|
|
5055
5224
|
"--cs-accent-strong": "#5A8A90",
|
|
5056
5225
|
"--cs-accent-bright": "#CDF4F9",
|
|
5057
5226
|
"--cs-accent-on": "#13282B",
|
|
5227
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
5058
5228
|
"--cs-accent-tint": "#EBFAFC",
|
|
5059
5229
|
"--cs-accent-ink": "#13282B",
|
|
5060
5230
|
"--cs-accent-glow": "rgba(192, 245, 252, 0.48)",
|
|
5061
5231
|
"--cs-accent-grad-a": "#C0F5FC",
|
|
5062
5232
|
"--cs-accent-grad-b": "#A3D768",
|
|
5063
5233
|
"--cs-color-text-accent": "#5A8A90",
|
|
5064
|
-
"--cs-color-link": "#
|
|
5234
|
+
"--cs-color-link": "#48787E"
|
|
5065
5235
|
},
|
|
5066
5236
|
"thuy": {
|
|
5067
5237
|
"--cs-accent": "#2EDBF7",
|
|
5068
5238
|
"--cs-accent-strong": "#108EA0",
|
|
5069
5239
|
"--cs-accent-bright": "#99EEFF",
|
|
5070
5240
|
"--cs-accent-on": "#012A30",
|
|
5241
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
5071
5242
|
"--cs-accent-tint": "#E7FBFF",
|
|
5072
5243
|
"--cs-accent-ink": "#012A30",
|
|
5073
5244
|
"--cs-accent-glow": "rgba(46, 219, 247, 0.48)",
|
|
5074
5245
|
"--cs-accent-grad-a": "#2EDBF7",
|
|
5075
5246
|
"--cs-accent-grad-b": "#A3D768",
|
|
5076
5247
|
"--cs-color-text-accent": "#108EA0",
|
|
5077
|
-
"--cs-color-link": "#
|
|
5248
|
+
"--cs-color-link": "#027B8B"
|
|
5078
5249
|
},
|
|
5079
5250
|
"thuy.ocean": {
|
|
5080
5251
|
"--cs-accent": "#004554",
|
|
5081
5252
|
"--cs-accent-strong": "#011A21",
|
|
5082
5253
|
"--cs-accent-bright": "#A8D3E0",
|
|
5083
5254
|
"--cs-accent-on": "#10282F",
|
|
5255
|
+
"--cs-accent-on-strong": "#FFFFFF",
|
|
5084
5256
|
"--cs-accent-tint": "#EAFAFF",
|
|
5085
5257
|
"--cs-accent-ink": "#10282F",
|
|
5086
5258
|
"--cs-accent-glow": "rgba(0, 69, 84, 0.48)",
|
|
@@ -5094,32 +5266,35 @@ const tokens = {
|
|
|
5094
5266
|
"--cs-accent-strong": "#7C8965",
|
|
5095
5267
|
"--cs-accent-bright": "#E5F1D2",
|
|
5096
5268
|
"--cs-accent-on": "#212617",
|
|
5269
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
5097
5270
|
"--cs-accent-tint": "#F4F9ED",
|
|
5098
5271
|
"--cs-accent-ink": "#212617",
|
|
5099
5272
|
"--cs-accent-glow": "rgba(225, 241, 200, 0.48)",
|
|
5100
5273
|
"--cs-accent-grad-a": "#E1F1C8",
|
|
5101
5274
|
"--cs-accent-grad-b": "#FCAF91",
|
|
5102
5275
|
"--cs-color-text-accent": "#7C8965",
|
|
5103
|
-
"--cs-color-link": "#
|
|
5276
|
+
"--cs-color-link": "#687451"
|
|
5104
5277
|
},
|
|
5105
5278
|
"moc": {
|
|
5106
5279
|
"--cs-accent": "#A3D768",
|
|
5107
5280
|
"--cs-accent-strong": "#629122",
|
|
5108
5281
|
"--cs-accent-bright": "#C5ED9C",
|
|
5109
5282
|
"--cs-accent-on": "#182A00",
|
|
5283
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
5110
5284
|
"--cs-accent-tint": "#EFFCE3",
|
|
5111
5285
|
"--cs-accent-ink": "#182A00",
|
|
5112
5286
|
"--cs-accent-glow": "rgba(163, 215, 104, 0.48)",
|
|
5113
5287
|
"--cs-accent-grad-a": "#A3D768",
|
|
5114
5288
|
"--cs-accent-grad-b": "#FCAF91",
|
|
5115
5289
|
"--cs-color-text-accent": "#629122",
|
|
5116
|
-
"--cs-color-link": "#
|
|
5290
|
+
"--cs-color-link": "#517B13"
|
|
5117
5291
|
},
|
|
5118
5292
|
"moc.forest": {
|
|
5119
5293
|
"--cs-accent": "#1A4A01",
|
|
5120
5294
|
"--cs-accent-strong": "#051D00",
|
|
5121
5295
|
"--cs-accent-bright": "#B0D8A3",
|
|
5122
5296
|
"--cs-accent-on": "#162A0E",
|
|
5297
|
+
"--cs-accent-on-strong": "#FFFFFF",
|
|
5123
5298
|
"--cs-accent-tint": "#ECFDE7",
|
|
5124
5299
|
"--cs-accent-ink": "#162A0E",
|
|
5125
5300
|
"--cs-accent-glow": "rgba(26, 74, 1, 0.48)",
|
|
@@ -5133,32 +5308,35 @@ const tokens = {
|
|
|
5133
5308
|
"--cs-accent-strong": "#947F65",
|
|
5134
5309
|
"--cs-accent-bright": "#FBE8D2",
|
|
5135
5310
|
"--cs-accent-on": "#2B2217",
|
|
5311
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
5136
5312
|
"--cs-accent-tint": "#FDF5ED",
|
|
5137
5313
|
"--cs-accent-ink": "#2B2217",
|
|
5138
5314
|
"--cs-accent-glow": "rgba(254, 229, 199, 0.48)",
|
|
5139
5315
|
"--cs-accent-grad-a": "#FEE5C7",
|
|
5140
5316
|
"--cs-accent-grad-b": "#2EDBF7",
|
|
5141
5317
|
"--cs-color-text-accent": "#947F65",
|
|
5142
|
-
"--cs-color-link": "#
|
|
5318
|
+
"--cs-color-link": "#826D54"
|
|
5143
5319
|
},
|
|
5144
5320
|
"kim": {
|
|
5145
5321
|
"--cs-accent": "#F3B958",
|
|
5146
5322
|
"--cs-accent-strong": "#AC780B",
|
|
5147
5323
|
"--cs-accent-bright": "#FDD89F",
|
|
5148
5324
|
"--cs-accent-on": "#312002",
|
|
5325
|
+
"--cs-accent-on-strong": "#1A1108",
|
|
5149
5326
|
"--cs-accent-tint": "#FFF5E7",
|
|
5150
5327
|
"--cs-accent-ink": "#312002",
|
|
5151
5328
|
"--cs-accent-glow": "rgba(243, 185, 88, 0.48)",
|
|
5152
5329
|
"--cs-accent-grad-a": "#F3B958",
|
|
5153
5330
|
"--cs-accent-grad-b": "#2EDBF7",
|
|
5154
5331
|
"--cs-color-text-accent": "#AC780B",
|
|
5155
|
-
"--cs-color-link": "#
|
|
5332
|
+
"--cs-color-link": "#966700"
|
|
5156
5333
|
},
|
|
5157
5334
|
"kim.steel": {
|
|
5158
5335
|
"--cs-accent": "#503901",
|
|
5159
5336
|
"--cs-accent-strong": "#1F1400",
|
|
5160
5337
|
"--cs-accent-bright": "#DCC8A2",
|
|
5161
5338
|
"--cs-accent-on": "#2D220E",
|
|
5339
|
+
"--cs-accent-on-strong": "#FFFFFF",
|
|
5162
5340
|
"--cs-accent-tint": "#FEF6E7",
|
|
5163
5341
|
"--cs-accent-ink": "#2D220E",
|
|
5164
5342
|
"--cs-accent-glow": "rgba(80, 57, 1, 0.48)",
|
|
@@ -5172,424 +5350,6 @@ const tokens = {
|
|
|
5172
5350
|
Object.assign(__ds_scope, { tokens, __ds_default_tokens_tokens_9gob35: tokens });
|
|
5173
5351
|
})(); } catch (e) { __ds_ns.__errors.push({ path: "tokens/tokens.js", error: String((e && e.message) || e) }); }
|
|
5174
5352
|
|
|
5175
|
-
// ui_kits/status-hub/StatusHub.jsx
|
|
5176
|
-
try { (() => {
|
|
5177
|
-
const { useState, useMemo } = React;
|
|
5178
|
-
const ICON = {
|
|
5179
|
-
search: "M11 4a7 7 0 1 0 4.2 12.6L20 21l1.4-1.4-4.4-4.4A7 7 0 0 0 11 4zm0 2a5 5 0 1 1 0 10 5 5 0 0 1 0-10z",
|
|
5180
|
-
close: "M6 6l12 12M18 6L6 18",
|
|
5181
|
-
sun: null,
|
|
5182
|
-
moon: null
|
|
5183
|
-
};
|
|
5184
|
-
function Glyph({ d, size = 18 }) {
|
|
5185
|
-
return /* @__PURE__ */ React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d }));
|
|
5186
|
-
}
|
|
5187
|
-
function SearchGlyph() {
|
|
5188
|
-
return /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: ICON.search }));
|
|
5189
|
-
}
|
|
5190
|
-
function ThemeGlyph({ dark }) {
|
|
5191
|
-
return dark ? /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "4" }), /* @__PURE__ */ React.createElement("path", { d: "M12 2v2M12 20v2M2 12h2M20 12h2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M19.1 4.9l-1.4 1.4M6.3 17.7l-1.4 1.4" })) : /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M20 13.5A8 8 0 1 1 10.5 4a6.5 6.5 0 0 0 9.5 9.5z" }));
|
|
5192
|
-
}
|
|
5193
|
-
const T = {
|
|
5194
|
-
en: {
|
|
5195
|
-
sub: "CyberSkill · one page, three lenses · Hiện Thực Hoá Ý Chí",
|
|
5196
|
-
updated: "Updated",
|
|
5197
|
-
portfolio: "Portfolio",
|
|
5198
|
-
projects: "Projects",
|
|
5199
|
-
inprog: "In progress",
|
|
5200
|
-
shipped: "Shipped",
|
|
5201
|
-
blocked: "Blocked",
|
|
5202
|
-
planned: "Planned",
|
|
5203
|
-
avg: "Avg progress",
|
|
5204
|
-
velocity: "Velocity · tasks shipped / week",
|
|
5205
|
-
perShipped: " shipped",
|
|
5206
|
-
nowTitle: "Now shipping",
|
|
5207
|
-
search: "Search projects…",
|
|
5208
|
-
view: "View",
|
|
5209
|
-
lenses: { board: "Board", table: "Table", releases: "Releases" },
|
|
5210
|
-
status: "Status",
|
|
5211
|
-
all: "All",
|
|
5212
|
-
of: "of",
|
|
5213
|
-
key: "Key",
|
|
5214
|
-
project: "Project",
|
|
5215
|
-
squad: "Squad",
|
|
5216
|
-
owner: "Owner",
|
|
5217
|
-
progress: "Progress",
|
|
5218
|
-
thisWeek: "This week",
|
|
5219
|
-
tasks: "Tasks",
|
|
5220
|
-
risk: "Risk",
|
|
5221
|
-
close: "Close",
|
|
5222
|
-
langBtn: "VN",
|
|
5223
|
-
theme: "Toggle theme"
|
|
5224
|
-
},
|
|
5225
|
-
vi: {
|
|
5226
|
-
sub: "CyberSkill · một trang, ba lăng kính · Hiện Thực Hoá Ý Chí",
|
|
5227
|
-
updated: "Cập nhật",
|
|
5228
|
-
portfolio: "Danh mục",
|
|
5229
|
-
projects: "Dự án",
|
|
5230
|
-
inprog: "Đang làm",
|
|
5231
|
-
shipped: "Đã ra mắt",
|
|
5232
|
-
blocked: "Bị chặn",
|
|
5233
|
-
planned: "Dự kiến",
|
|
5234
|
-
avg: "Tiến độ TB",
|
|
5235
|
-
velocity: "Tốc độ · task giao / tuần",
|
|
5236
|
-
perShipped: " đã giao",
|
|
5237
|
-
nowTitle: "Đang ra mắt",
|
|
5238
|
-
search: "Tìm dự án…",
|
|
5239
|
-
view: "Chế độ xem",
|
|
5240
|
-
lenses: { board: "Bảng công việc", table: "Bảng dữ liệu", releases: "Phát hành" },
|
|
5241
|
-
status: "Trạng thái",
|
|
5242
|
-
all: "Tất cả",
|
|
5243
|
-
of: "trên",
|
|
5244
|
-
key: "Mã",
|
|
5245
|
-
project: "Dự án",
|
|
5246
|
-
squad: "Nhóm",
|
|
5247
|
-
owner: "Phụ trách",
|
|
5248
|
-
progress: "Tiến độ",
|
|
5249
|
-
thisWeek: "Tuần này",
|
|
5250
|
-
tasks: "Công việc",
|
|
5251
|
-
risk: "Rủi ro",
|
|
5252
|
-
close: "Đóng",
|
|
5253
|
-
langBtn: "EN",
|
|
5254
|
-
theme: "Đổi giao diện"
|
|
5255
|
-
}
|
|
5256
|
-
};
|
|
5257
|
-
const statusLabels = (t) => ({ done: t.shipped, active: t.inprog, hold: t.blocked, todo: t.planned });
|
|
5258
|
-
function Segments({ seg, className }) {
|
|
5259
|
-
return /* @__PURE__ */ React.createElement("div", { className }, seg.done > 0 && /* @__PURE__ */ React.createElement("i", { className: "seg-done", style: { width: seg.done + "%" } }), seg.active > 0 && /* @__PURE__ */ React.createElement("i", { className: "seg-active", style: { width: seg.active + "%" } }), seg.hold > 0 && /* @__PURE__ */ React.createElement("i", { className: "seg-hold", style: { width: seg.hold + "%" } }), seg.todo > 0 && /* @__PURE__ */ React.createElement("i", { className: "seg-todo", style: { width: seg.todo + "%" } }));
|
|
5260
|
-
}
|
|
5261
|
-
function Drawer({ p, onClose, t }) {
|
|
5262
|
-
if (!p) return null;
|
|
5263
|
-
const SL = statusLabels(t);
|
|
5264
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "scrim", onClick: onClose }), /* @__PURE__ */ React.createElement("aside", { className: "drawer", role: "dialog", "aria-modal": "true", "aria-label": p.name }, /* @__PURE__ */ React.createElement("div", { className: "dw-h" }, /* @__PURE__ */ React.createElement("div", { className: "row" }, /* @__PURE__ */ React.createElement("span", { className: "mono muted", style: { fontWeight: 700 } }, p.key), /* @__PURE__ */ React.createElement("span", { className: "pill " + p.status }, SL[p.status]), /* @__PURE__ */ React.createElement("button", { className: "dw-x", onClick: onClose, "aria-label": t.close }, /* @__PURE__ */ React.createElement(Glyph, { d: ICON.close, size: 20 }))), /* @__PURE__ */ React.createElement("h2", null, p.name)), /* @__PURE__ */ React.createElement("div", { className: "dw-b" }, /* @__PURE__ */ React.createElement("p", { className: "muted", style: { marginTop: 0 } }, p.blurb), /* @__PURE__ */ React.createElement("dl", { className: "meta" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, t.owner), /* @__PURE__ */ React.createElement("dd", null, p.owner)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, t.squad), /* @__PURE__ */ React.createElement("dd", null, p.squad)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, t.progress), /* @__PURE__ */ React.createElement("dd", null, p.pct, "%")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, t.updated), /* @__PURE__ */ React.createElement("dd", null, p.updated))), /* @__PURE__ */ React.createElement(Segments, { seg: p.seg, className: "minibar" }), /* @__PURE__ */ React.createElement("div", { className: "sect" }, /* @__PURE__ */ React.createElement("h3", null, t.tasks), /* @__PURE__ */ React.createElement("div", { className: "task-chips" }, p.tasks.map((tk) => /* @__PURE__ */ React.createElement("span", { key: tk[0], className: "chip " + tk[2], title: tk[1] }, tk[0]))), /* @__PURE__ */ React.createElement("ul", { className: "tasks" }, p.tasks.map((tk) => /* @__PURE__ */ React.createElement("li", { key: tk[0] }, tk[1], " — ", /* @__PURE__ */ React.createElement("span", { className: "muted" }, SL[tk[2]]))))), p.risk ? /* @__PURE__ */ React.createElement("div", { className: "sect" }, /* @__PURE__ */ React.createElement("h3", null, t.risk), /* @__PURE__ */ React.createElement("div", { className: "risk" }, p.risk)) : null)));
|
|
5265
|
-
}
|
|
5266
|
-
function StatusHub() {
|
|
5267
|
-
const D = window.SH_DATA;
|
|
5268
|
-
const [dark, setDark] = useState(false);
|
|
5269
|
-
const [lang, setLang] = useState("en");
|
|
5270
|
-
const [lens, setLens] = useState("board");
|
|
5271
|
-
const [q, setQ] = useState("");
|
|
5272
|
-
const [facet, setFacet] = useState("all");
|
|
5273
|
-
const [kpi, setKpi] = useState(null);
|
|
5274
|
-
const [sel, setSel] = useState(null);
|
|
5275
|
-
const t = T[lang];
|
|
5276
|
-
const SL = statusLabels(t);
|
|
5277
|
-
const VI = lang === "vi" ? window.SH_VI || null : null;
|
|
5278
|
-
const loc = (p) => VI ? {
|
|
5279
|
-
...p,
|
|
5280
|
-
blurb: VI.blurbs[p.key] || p.blurb,
|
|
5281
|
-
risk: VI.risks[p.key] != null ? VI.risks[p.key] : p.risk,
|
|
5282
|
-
owner: VI.owners[p.owner] || p.owner,
|
|
5283
|
-
squad: VI.squads[p.squad] || p.squad,
|
|
5284
|
-
updated: VI.updatedRel[p.updated] || p.updated
|
|
5285
|
-
} : p;
|
|
5286
|
-
const counts = useMemo(() => {
|
|
5287
|
-
const c = { active: 0, done: 0, hold: 0 };
|
|
5288
|
-
D.projects.forEach((p) => {
|
|
5289
|
-
if (c[p.status] != null) c[p.status]++;
|
|
5290
|
-
});
|
|
5291
|
-
const avg = Math.round(D.projects.reduce((a, p) => a + p.pct, 0) / D.projects.length);
|
|
5292
|
-
return { total: D.projects.length, ...c, avg };
|
|
5293
|
-
}, [D]);
|
|
5294
|
-
const shown = useMemo(() => {
|
|
5295
|
-
const needle = q.trim().toLowerCase();
|
|
5296
|
-
const eff = kpi || (facet === "all" ? null : facet);
|
|
5297
|
-
return D.projects.filter((p) => {
|
|
5298
|
-
if (eff && p.status !== eff) return false;
|
|
5299
|
-
if (!needle) return true;
|
|
5300
|
-
return (p.name + " " + p.key + " " + p.blurb).toLowerCase().includes(needle);
|
|
5301
|
-
});
|
|
5302
|
-
}, [D, q, facet, kpi]);
|
|
5303
|
-
const maxV = Math.max(...D.velocity.map((v) => v.n));
|
|
5304
|
-
const toggleKpi = (k) => setKpi((cur) => cur === k ? null : k);
|
|
5305
|
-
return /* @__PURE__ */ React.createElement("div", { className: "sh", lang, "data-theme": dark ? "dark" : void 0 }, /* @__PURE__ */ React.createElement("header", { className: "hd" }, /* @__PURE__ */ React.createElement("div", { className: "hd-in" }, /* @__PURE__ */ React.createElement("div", { className: "hd-id" }, /* @__PURE__ */ React.createElement("span", { className: "hd-mark" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/logo-mark.svg", alt: "" })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h1", null, "Status Hub"), /* @__PURE__ */ React.createElement("p", { className: "hd-sub" }, t.sub))), /* @__PURE__ */ React.createElement("div", { className: "hd-side" }, /* @__PURE__ */ React.createElement("p", { className: "hd-meta" }, t.updated, /* @__PURE__ */ React.createElement("br", null), VI ? VI.updated : D.updated), /* @__PURE__ */ React.createElement("button", { className: "btn ghost", onClick: () => setLang((l) => l === "en" ? "vi" : "en"), "aria-label": "Toggle language", style: { fontWeight: 700, fontSize: 13 } }, t.langBtn), /* @__PURE__ */ React.createElement("button", { className: "btn ghost", onClick: () => setDark((v) => !v), "aria-label": t.theme }, /* @__PURE__ */ React.createElement(ThemeGlyph, { dark }))))), /* @__PURE__ */ React.createElement("div", { className: "wrap" }, /* @__PURE__ */ React.createElement("div", { className: "deck" }, /* @__PURE__ */ React.createElement("section", { className: "panel" }, /* @__PURE__ */ React.createElement("h2", null, t.portfolio), /* @__PURE__ */ React.createElement("div", { className: "kpis" }, /* @__PURE__ */ React.createElement("button", { className: "kpi", "data-on": kpi === null ? "1" : "0", onClick: () => setKpi(null) }, /* @__PURE__ */ React.createElement("b", null, counts.total), /* @__PURE__ */ React.createElement("span", null, t.projects)), /* @__PURE__ */ React.createElement("button", { className: "kpi", "data-on": kpi === "active" ? "1" : "0", onClick: () => toggleKpi("active") }, /* @__PURE__ */ React.createElement("b", null, counts.active), /* @__PURE__ */ React.createElement("span", null, t.inprog)), /* @__PURE__ */ React.createElement("button", { className: "kpi", "data-on": kpi === "done" ? "1" : "0", onClick: () => toggleKpi("done") }, /* @__PURE__ */ React.createElement("b", null, counts.done), /* @__PURE__ */ React.createElement("span", null, t.shipped)), /* @__PURE__ */ React.createElement("button", { className: "kpi", "data-on": kpi === "hold" ? "1" : "0", onClick: () => toggleKpi("hold") }, /* @__PURE__ */ React.createElement("b", null, counts.hold), /* @__PURE__ */ React.createElement("span", null, t.blocked)), /* @__PURE__ */ React.createElement("button", { className: "kpi", onClick: () => setKpi(null) }, /* @__PURE__ */ React.createElement("b", null, counts.avg, "%"), /* @__PURE__ */ React.createElement("span", null, t.avg))), /* @__PURE__ */ React.createElement(Segments, { seg: { done: 60, active: 22, hold: 6, todo: 12 }, className: "bar-seg" }), /* @__PURE__ */ React.createElement("div", { className: "legend" }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("i", { className: "dot", style: { background: "var(--cs-color-semantic-success)" } }), t.shipped), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("i", { className: "dot", style: { background: "var(--cs-color-brand-ochre)" } }), t.inprog), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("i", { className: "dot", style: { background: "var(--cs-color-semantic-danger)" } }), t.blocked), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("i", { className: "dot", style: { background: "var(--cs-status-todo)" } }), t.planned))), /* @__PURE__ */ React.createElement("section", { className: "panel" }, /* @__PURE__ */ React.createElement("h2", null, t.velocity), /* @__PURE__ */ React.createElement("div", { className: "spark" }, D.velocity.map((v) => /* @__PURE__ */ React.createElement("a", { key: v.wk, title: v.n + t.perShipped }, /* @__PURE__ */ React.createElement("b", null, v.n), /* @__PURE__ */ React.createElement("i", { style: { height: v.n / maxV * 72 + "px" } }), /* @__PURE__ */ React.createElement("span", null, v.wk)))))), /* @__PURE__ */ React.createElement("div", { className: "now" }, /* @__PURE__ */ React.createElement("h2", null, t.nowTitle), lang === "vi" ? /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("b", null, "v1.6.0 — Liên kết Glass + gói Ngũ Hành v2."), " Liên kết component ", /* @__PURE__ */ React.createElement("span", { className: "mono" }, ".cs-surface-*"), " (tùy chọn), trình kiểm 0 cảnh báo trên cả 15 gói, và kiểm tra lại APCA cho chế độ tối. Ra mắt tuần này.") : /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("b", null, "v1.6.0 — Glass bindings + element-pack v2."), " Opt-in ", /* @__PURE__ */ React.createElement("span", { className: "mono" }, ".cs-surface-*"), " component bindings, a zero-warning verifier across all 15 packs, and a dark-mode APCA re-check. Ships this week.")), /* @__PURE__ */ React.createElement("div", { className: "bar" }, /* @__PURE__ */ React.createElement("div", { className: "bar-top" }, /* @__PURE__ */ React.createElement("label", { className: "srch" }, /* @__PURE__ */ React.createElement(SearchGlyph, null), /* @__PURE__ */ React.createElement("input", { placeholder: t.search, value: q, onChange: (e) => setQ(e.target.value) })), /* @__PURE__ */ React.createElement("div", { className: "lenses", role: "tablist", "aria-label": t.view }, ["board", "table", "releases"].map((l) => /* @__PURE__ */ React.createElement("button", { key: l, className: "ln", role: "tab", "aria-selected": lens === l, onClick: () => setLens(l) }, t.lenses[l]))), /* @__PURE__ */ React.createElement("label", { className: "facet" }, t.status, /* @__PURE__ */ React.createElement("select", { value: kpi || facet, onChange: (e) => {
|
|
5306
|
-
setFacet(e.target.value);
|
|
5307
|
-
setKpi(null);
|
|
5308
|
-
} }, /* @__PURE__ */ React.createElement("option", { value: "all" }, t.all), /* @__PURE__ */ React.createElement("option", { value: "active" }, t.inprog), /* @__PURE__ */ React.createElement("option", { value: "done" }, t.shipped), /* @__PURE__ */ React.createElement("option", { value: "hold" }, t.blocked))), /* @__PURE__ */ React.createElement("span", { className: "cnt" }, shown.length, " ", t.of, " ", D.projects.length))), lens === "board" && /* @__PURE__ */ React.createElement("div", { className: "grid" }, shown.map((p0) => {
|
|
5309
|
-
const p = loc(p0);
|
|
5310
|
-
return /* @__PURE__ */ React.createElement("button", { className: "card" + (p.status === "active" ? " hot" : ""), key: p.key, onClick: () => setSel(p0) }, /* @__PURE__ */ React.createElement("div", { className: "card-h" }, /* @__PURE__ */ React.createElement("h3", null, /* @__PURE__ */ React.createElement("span", { className: "k mono" }, p.key), " · ", p.name), /* @__PURE__ */ React.createElement("span", { className: "pct" }, p.pct, "%")), /* @__PURE__ */ React.createElement("p", null, p.blurb), /* @__PURE__ */ React.createElement(Segments, { seg: p.seg, className: "minibar" }), /* @__PURE__ */ React.createElement("div", { className: "task-chips" }, /* @__PURE__ */ React.createElement("span", { className: "pill " + p.status }, SL[p.status]), p.tasks.slice(0, 3).map((tk) => /* @__PURE__ */ React.createElement("span", { key: tk[0], className: "chip " + tk[2] }, tk[0]))));
|
|
5311
|
-
})), lens === "table" && /* @__PURE__ */ React.createElement("div", { className: "tbl-wrap" }, /* @__PURE__ */ React.createElement("table", { className: "tbl" }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, t.key), /* @__PURE__ */ React.createElement("th", null, t.project), /* @__PURE__ */ React.createElement("th", null, t.squad), /* @__PURE__ */ React.createElement("th", null, t.owner), /* @__PURE__ */ React.createElement("th", null, t.progress), /* @__PURE__ */ React.createElement("th", null, t.status))), /* @__PURE__ */ React.createElement("tbody", null, shown.map((p0) => {
|
|
5312
|
-
const p = loc(p0);
|
|
5313
|
-
return /* @__PURE__ */ React.createElement("tr", { key: p.key, onClick: () => setSel(p0) }, /* @__PURE__ */ React.createElement("td", { className: "mono", style: { fontWeight: 700 } }, p.key), /* @__PURE__ */ React.createElement("td", null, p.name), /* @__PURE__ */ React.createElement("td", null, p.squad), /* @__PURE__ */ React.createElement("td", null, p.owner), /* @__PURE__ */ React.createElement("td", null, p.pct, "%"), /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement("span", { className: "pill " + p.status }, SL[p.status])));
|
|
5314
|
-
})))), lens === "releases" && /* @__PURE__ */ React.createElement("div", { className: "rels" }, D.releases.map((r) => /* @__PURE__ */ React.createElement("div", { className: "rel" + (r.now ? " now" : ""), key: r.ver }, /* @__PURE__ */ React.createElement("div", { className: "tick" }, r.done ? "✓" : "→"), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "rel-h" }, /* @__PURE__ */ React.createElement("b", null, r.ver), /* @__PURE__ */ React.createElement("span", { className: "pill " + (r.now ? "active" : "done") }, r.now ? t.thisWeek : t.shipped), /* @__PURE__ */ React.createElement("span", { className: "muted" }, VI ? VI.relWhen[r.ver] || r.when : r.when)), /* @__PURE__ */ React.createElement("div", { className: "rel-sec" }, /* @__PURE__ */ React.createElement("h4", null, VI ? VI.relTitles[r.ver] || r.title : r.title), /* @__PURE__ */ React.createElement("ul", null, (VI ? VI.relItems[r.ver] || r.items : r.items).map((it, i) => /* @__PURE__ */ React.createElement("li", { key: i }, it)))))))), /* @__PURE__ */ React.createElement("p", { className: "ft" }, lang === "vi" ? /* @__PURE__ */ React.createElement(React.Fragment, null, "CyberSkill Status Hub — bản tái hiện UI kit của design system. Dựng trên token ", /* @__PURE__ */ React.createElement("span", { className: "mono" }, "styles.css"), ". Turn Your Will Into Real.") : /* @__PURE__ */ React.createElement(React.Fragment, null, "CyberSkill Status Hub — a design-system UI kit recreation. Built on ", /* @__PURE__ */ React.createElement("span", { className: "mono" }, "styles.css"), " tokens. Turn Your Will Into Real."))), /* @__PURE__ */ React.createElement(Drawer, { p: sel && loc(sel), onClose: () => setSel(null), t }));
|
|
5315
|
-
}
|
|
5316
|
-
window.StatusHub = StatusHub;
|
|
5317
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/status-hub/StatusHub.jsx", error: String((e && e.message) || e) }); }
|
|
5318
|
-
|
|
5319
|
-
// ui_kits/status-hub/data.js
|
|
5320
|
-
try { (() => {
|
|
5321
|
-
window.SH_DATA = {
|
|
5322
|
-
updated: "18 Jul 2026, 13:20 ICT",
|
|
5323
|
-
velocity: [
|
|
5324
|
-
{ wk: "W23", n: 4 },
|
|
5325
|
-
{ wk: "W24", n: 7 },
|
|
5326
|
-
{ wk: "W25", n: 5 },
|
|
5327
|
-
{ wk: "W26", n: 9 },
|
|
5328
|
-
{ wk: "W27", n: 6 },
|
|
5329
|
-
{ wk: "W28", n: 11 },
|
|
5330
|
-
{ wk: "W29", n: 8 }
|
|
5331
|
-
],
|
|
5332
|
-
projects: [
|
|
5333
|
-
{
|
|
5334
|
-
key: "CDS",
|
|
5335
|
-
name: "Design System v1.6",
|
|
5336
|
-
status: "active",
|
|
5337
|
-
pct: 72,
|
|
5338
|
-
blurb: "Liquid Glass component bindings + 15 element packs.",
|
|
5339
|
-
seg: { done: 62, active: 20, hold: 4, todo: 14 },
|
|
5340
|
-
owner: "Design Lead",
|
|
5341
|
-
updated: "2h ago",
|
|
5342
|
-
squad: "Platform",
|
|
5343
|
-
tasks: [["TASK-DS-041", "Glass surface bindings", "done"], ["TASK-DS-047", "Element-pack verifier v2", "active"], ["TASK-DS-052", "Dark-mode APCA re-check", "active"], ["TASK-DS-055", "Figma variable sync", "todo"]],
|
|
5344
|
-
risk: ""
|
|
5345
|
-
},
|
|
5346
|
-
{
|
|
5347
|
-
key: "GENIE",
|
|
5348
|
-
name: "Lumi Genie Chat",
|
|
5349
|
-
status: "active",
|
|
5350
|
-
pct: 58,
|
|
5351
|
-
blurb: "Keyless Claude proxy; scripted wish-capture flow.",
|
|
5352
|
-
seg: { done: 48, active: 24, hold: 8, todo: 20 },
|
|
5353
|
-
owner: "AI Lead",
|
|
5354
|
-
updated: "5h ago",
|
|
5355
|
-
squad: "AI",
|
|
5356
|
-
tasks: [["TASK-CHAR-026", "In-chat wish flow", "done"], ["TASK-CHAR-030", "One-time mascot hint", "active"], ["TASK-CHAR-021", "Golden-genie GLB", "hold"]],
|
|
5357
|
-
risk: "GLB art asset is a hard external dependency — commission not yet confirmed (TASK-CHAR-021)."
|
|
5358
|
-
},
|
|
5359
|
-
{
|
|
5360
|
-
key: "SITE",
|
|
5361
|
-
name: "cyberskill.world",
|
|
5362
|
-
status: "active",
|
|
5363
|
-
pct: 84,
|
|
5364
|
-
blurb: "Bilingual storytelling landing; lead capture live.",
|
|
5365
|
-
seg: { done: 78, active: 12, hold: 0, todo: 10 },
|
|
5366
|
-
owner: "Web Lead",
|
|
5367
|
-
updated: "1h ago",
|
|
5368
|
-
squad: "Growth",
|
|
5369
|
-
tasks: [["TASK-BIZ-003", "Lead pipeline e2e", "done"], ["TASK-SEO-012", "llms.txt route", "done"], ["TASK-CTA-020", "Teardown funnel polish", "active"]],
|
|
5370
|
-
risk: ""
|
|
5371
|
-
},
|
|
5372
|
-
{
|
|
5373
|
-
key: "OS",
|
|
5374
|
-
name: "CyberOS Agent Spine",
|
|
5375
|
-
status: "hold",
|
|
5376
|
-
pct: 41,
|
|
5377
|
-
blurb: "BRAIN memory store + HITL acceptance gates.",
|
|
5378
|
-
seg: { done: 36, active: 8, hold: 30, todo: 26 },
|
|
5379
|
-
owner: "Eng Lead",
|
|
5380
|
-
updated: "1d ago",
|
|
5381
|
-
squad: "Platform",
|
|
5382
|
-
tasks: [["TASK-OS-018", "Gate runner", "done"], ["TASK-OS-022", "Memory compaction", "hold"], ["TASK-OS-025", "Provenance ledger", "hold"]],
|
|
5383
|
-
risk: "Blocked on infra: provenance ledger needs the signing service (target Q1 2027)."
|
|
5384
|
-
},
|
|
5385
|
-
{
|
|
5386
|
-
key: "AUDIT",
|
|
5387
|
-
name: "DS Audit Framework",
|
|
5388
|
-
status: "done",
|
|
5389
|
-
pct: 100,
|
|
5390
|
-
blurb: "125-criteria vendor-neutral audit; L3 tier held.",
|
|
5391
|
-
seg: { done: 100, active: 0, hold: 0, todo: 0 },
|
|
5392
|
-
owner: "DesignOps",
|
|
5393
|
-
updated: "3d ago",
|
|
5394
|
-
squad: "Platform",
|
|
5395
|
-
tasks: [["TASK-AUD-009", "Deterministic engine", "done"], ["TASK-AUD-011", "No-regression CI gate", "done"]],
|
|
5396
|
-
risk: ""
|
|
5397
|
-
},
|
|
5398
|
-
{
|
|
5399
|
-
key: "TOKENS",
|
|
5400
|
-
name: "Token Pipeline",
|
|
5401
|
-
status: "active",
|
|
5402
|
-
pct: 66,
|
|
5403
|
-
blurb: "DTCG → CSS/TS/Swift/Kotlin/Flutter/Figma.",
|
|
5404
|
-
seg: { done: 58, active: 22, hold: 0, todo: 20 },
|
|
5405
|
-
owner: "Eng Lead",
|
|
5406
|
-
updated: "6h ago",
|
|
5407
|
-
squad: "Platform",
|
|
5408
|
-
tasks: [["TASK-TOK-014", "Contrast grid export", "done"], ["TASK-TOK-019", "Flutter target", "active"], ["TASK-TOK-021", "Provenance JSON", "todo"]],
|
|
5409
|
-
risk: ""
|
|
5410
|
-
}
|
|
5411
|
-
],
|
|
5412
|
-
releases: [
|
|
5413
|
-
{
|
|
5414
|
-
ver: "v1.6.0",
|
|
5415
|
-
when: "This week",
|
|
5416
|
-
now: true,
|
|
5417
|
-
done: false,
|
|
5418
|
-
title: "Glass bindings + element-pack v2",
|
|
5419
|
-
items: ["Opt-in .cs-surface-* component bindings", "Element-pack verifier v2 (0 warnings across 15)", "Dark-mode APCA re-check"]
|
|
5420
|
-
},
|
|
5421
|
-
{
|
|
5422
|
-
ver: "v1.5.1",
|
|
5423
|
-
when: "04 Jul 2026",
|
|
5424
|
-
now: false,
|
|
5425
|
-
done: true,
|
|
5426
|
-
title: "15 element packs ship",
|
|
5427
|
-
items: ["All 15 packs promoted to shipped", "Per-pack screenshot regression", "Gallery build script"]
|
|
5428
|
-
},
|
|
5429
|
-
{
|
|
5430
|
-
ver: "v1.4.0",
|
|
5431
|
-
when: "13 Jun 2026",
|
|
5432
|
-
now: false,
|
|
5433
|
-
done: true,
|
|
5434
|
-
title: "Liquid Glass in code",
|
|
5435
|
-
items: ["--cs-glass-* + --cs-depth-* scalars", "glass.css material layer", "Reduced-transparency fallbacks"]
|
|
5436
|
-
},
|
|
5437
|
-
{
|
|
5438
|
-
ver: "v1.3.0",
|
|
5439
|
-
when: "14 May 2026",
|
|
5440
|
-
now: false,
|
|
5441
|
-
done: true,
|
|
5442
|
-
title: "Brand assets + Logo",
|
|
5443
|
-
items: ["Official master mark ships", "React <Logo> from master artwork", "Light becomes default theme"]
|
|
5444
|
-
}
|
|
5445
|
-
]
|
|
5446
|
-
};
|
|
5447
|
-
window.SH_VI = {
|
|
5448
|
-
updated: "18/07/2026, 13:20 ICT",
|
|
5449
|
-
blurbs: { CDS: "Liên kết component Liquid Glass + 15 gói Ngũ Hành.", GENIE: "Proxy Claude không cần khóa; luồng tiếp nhận điều ước theo kịch bản.", SITE: "Trang giới thiệu song ngữ kể chuyện; thu lead đang chạy.", OS: "Kho nhớ BRAIN + cổng nghiệm thu có con người.", AUDIT: "Bộ kiểm định trung lập 125 tiêu chí; giữ hạng L3.", TOKENS: "DTCG → CSS/TS/Swift/Kotlin/Flutter/Figma." },
|
|
5450
|
-
risks: { GENIE: "Tài sản GLB là phụ thuộc ngoài khó — chưa chốt đặt hàng (TASK-CHAR-021).", OS: "Bị chặn bởi hạ tầng: sổ cái nguồn gốc cần dịch vụ ký số (mục tiêu Q1 2027)." },
|
|
5451
|
-
owners: { "Design Lead": "Trưởng thiết kế", "AI Lead": "Trưởng AI", "Web Lead": "Trưởng web", "Eng Lead": "Trưởng kỹ thuật", "DesignOps": "DesignOps" },
|
|
5452
|
-
squads: { Platform: "Nền tảng", AI: "AI", Growth: "Tăng trưởng" },
|
|
5453
|
-
updatedRel: { "2h ago": "2 giờ trước", "5h ago": "5 giờ trước", "1h ago": "1 giờ trước", "1d ago": "1 ngày trước", "3d ago": "3 ngày trước", "6h ago": "6 giờ trước" },
|
|
5454
|
-
relWhen: { "v1.6.0": "Tuần này", "v1.5.1": "04/07/2026", "v1.4.0": "13/06/2026", "v1.3.0": "14/05/2026" },
|
|
5455
|
-
relTitles: { "v1.6.0": "Liên kết Glass + gói Ngũ Hành v2", "v1.5.1": "15 gói Ngũ Hành ra mắt", "v1.4.0": "Liquid Glass vào mã", "v1.3.0": "Tài sản thương hiệu + Logo" },
|
|
5456
|
-
relItems: { "v1.6.0": ["Liên kết component .cs-surface-* (tùy chọn)", "Trình kiểm gói Ngũ Hành v2 (0 cảnh báo trên 15 gói)", "Kiểm tra lại APCA cho chế độ tối"], "v1.5.1": ["Cả 15 gói lên trạng thái đã ra mắt", "Regression ảnh chụp từng gói", "Script dựng gallery"], "v1.4.0": ["Scalar --cs-glass-* + --cs-depth-*", "Lớp chất liệu glass.css", "Fallback khi giảm độ trong suốt"], "v1.3.0": ["Master mark chính thức ra mắt", "<Logo> React từ artwork gốc", "Light thành theme mặc định"] }
|
|
5457
|
-
};
|
|
5458
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/status-hub/data.js", error: String((e && e.message) || e) }); }
|
|
5459
|
-
|
|
5460
|
-
// ui_kits/website/Website.jsx
|
|
5461
|
-
try { (() => {
|
|
5462
|
-
const { useState, useRef, useEffect } = React;
|
|
5463
|
-
function I({ name, size = 20, stroke = 1.9 }) {
|
|
5464
|
-
const p = {
|
|
5465
|
-
sparkle: "M12 3l1.8 5.4L19 10l-5.2 1.6L12 17l-1.8-5.4L5 10l5.2-1.6z",
|
|
5466
|
-
chat: "M4 5h16v11H8l-4 4z",
|
|
5467
|
-
check: "M4 12.5l5 5 11-11",
|
|
5468
|
-
"arrow-right": "M5 12h14M13 6l6 6-6 6",
|
|
5469
|
-
close: "M6 6l12 12M18 6L6 18",
|
|
5470
|
-
sun: "M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8zM12 2v2M12 20v2M2 12h2M20 12h2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M19.1 4.9l-1.4 1.4M6.3 17.7l-1.4 1.4",
|
|
5471
|
-
moon: "M20 13.5A8 8 0 1 1 10.5 4a6.5 6.5 0 0 0 9.5 9.5z"
|
|
5472
|
-
}[name];
|
|
5473
|
-
return /* @__PURE__ */ React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: p }));
|
|
5474
|
-
}
|
|
5475
|
-
function Chat({ t, seed, onClose }) {
|
|
5476
|
-
const [msgs, setMsgs] = useState([{ who: "bot", text: t.genie.greeting }]);
|
|
5477
|
-
const [val, setVal] = useState("");
|
|
5478
|
-
const bodyRef = useRef(null);
|
|
5479
|
-
useEffect(() => {
|
|
5480
|
-
if (seed) send(seed);
|
|
5481
|
-
}, []);
|
|
5482
|
-
useEffect(() => {
|
|
5483
|
-
const b = bodyRef.current;
|
|
5484
|
-
if (b) b.scrollTop = b.scrollHeight;
|
|
5485
|
-
}, [msgs]);
|
|
5486
|
-
function send(text) {
|
|
5487
|
-
const m = (text || "").trim();
|
|
5488
|
-
if (!m) return;
|
|
5489
|
-
setMsgs((cur) => [...cur, { who: "me", text: m }]);
|
|
5490
|
-
setVal("");
|
|
5491
|
-
setTimeout(() => setMsgs((cur) => [...cur, { who: "bot", text: "A wish worth granting. I’ll hand it to the team — first, what should I call you?" }]), 500);
|
|
5492
|
-
}
|
|
5493
|
-
return /* @__PURE__ */ React.createElement("div", { className: "chat" }, /* @__PURE__ */ React.createElement("div", { className: "chat-h" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/lumi-poster.webp", alt: "" }), /* @__PURE__ */ React.createElement("b", null, t.genie.title), /* @__PURE__ */ React.createElement("button", { onClick: onClose, "aria-label": "Close chat" }, /* @__PURE__ */ React.createElement(I, { name: "close", size: 18 }))), /* @__PURE__ */ React.createElement("div", { className: "chat-b", ref: bodyRef }, msgs.map((m, i) => /* @__PURE__ */ React.createElement("div", { key: i, className: "msg " + m.who }, m.text)), msgs.length === 1 && /* @__PURE__ */ React.createElement("div", { className: "chips" }, t.genie.chips.map((c) => /* @__PURE__ */ React.createElement("button", { key: c, onClick: () => send(c) }, c)))), /* @__PURE__ */ React.createElement("div", { className: "chat-f" }, /* @__PURE__ */ React.createElement("input", { value: val, onChange: (e) => setVal(e.target.value), onKeyDown: (e) => e.key === "Enter" && send(val), placeholder: t.genie.placeholder }), /* @__PURE__ */ React.createElement("button", { onClick: () => send(val) }, t.genie.send)));
|
|
5494
|
-
}
|
|
5495
|
-
function Website() {
|
|
5496
|
-
const [loc, setLoc] = useState("en");
|
|
5497
|
-
const [dark, setDark] = useState(false);
|
|
5498
|
-
const [chat, setChat] = useState(false);
|
|
5499
|
-
const [seed, setSeed] = useState("");
|
|
5500
|
-
const [wish, setWish] = useState("");
|
|
5501
|
-
const [sent, setSent] = useState(false);
|
|
5502
|
-
const t = window.SITE_COPY[loc];
|
|
5503
|
-
function openChat(s) {
|
|
5504
|
-
setSeed(s || "");
|
|
5505
|
-
setChat(true);
|
|
5506
|
-
}
|
|
5507
|
-
return /* @__PURE__ */ React.createElement("div", { className: "site", "data-theme": dark ? "dark" : void 0 }, /* @__PURE__ */ React.createElement("header", { className: "hdr cs-surface-light" }, /* @__PURE__ */ React.createElement("div", { className: "container hdr-in" }, /* @__PURE__ */ React.createElement("a", { className: "brand", href: "#top" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/logo-mark.svg", alt: "" }), "CyberSkill"), /* @__PURE__ */ React.createElement("nav", { className: "nav" }, t.nav.map((n, i) => /* @__PURE__ */ React.createElement("a", { key: i, href: "#" + ["services", "work", "process", "careers", "contact"][i] }, n))), /* @__PURE__ */ React.createElement("div", { className: "hdr-side" }, /* @__PURE__ */ React.createElement("button", { className: "mini", onClick: () => setLoc(loc === "en" ? "vi" : "en") }, t.langLabel), /* @__PURE__ */ React.createElement("button", { className: "mini icon", onClick: () => setDark(!dark), "aria-label": "Toggle theme" }, /* @__PURE__ */ React.createElement(I, { name: dark ? "sun" : "moon", size: 18 })), /* @__PURE__ */ React.createElement("button", { className: "mini", style: { background: "var(--cs-color-brand-umber)", color: "#fff", borderColor: "transparent" }, onClick: () => openChat("") }, t.talk)))), /* @__PURE__ */ React.createElement("section", { className: "hero", id: "top" }, /* @__PURE__ */ React.createElement("div", { className: "container hero-in" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "eyebrow" }, t.eyebrow), /* @__PURE__ */ React.createElement("h1", null, t.slogan), /* @__PURE__ */ React.createElement("div", { className: "vn" }, t.sub), /* @__PURE__ */ React.createElement("p", { className: "lead" }, t.lead), /* @__PURE__ */ React.createElement("div", { className: "wish" }, /* @__PURE__ */ React.createElement("input", { value: wish, onChange: (e) => setWish(e.target.value), onKeyDown: (e) => e.key === "Enter" && openChat(wish), placeholder: t.wishPlaceholder, "aria-label": t.wishPlaceholder }), /* @__PURE__ */ React.createElement("button", { className: "btn-gold", onClick: () => openChat(wish) }, /* @__PURE__ */ React.createElement(I, { name: "sparkle", size: 18 }), t.wishCta), /* @__PURE__ */ React.createElement("button", { className: "btn-ghost-light", onClick: () => openChat("") }, t.talk))), /* @__PURE__ */ React.createElement("div", { className: "hero-art" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/lumi-poster.webp", alt: "Lumi, the golden genie" })))), /* @__PURE__ */ React.createElement("div", { className: "marq" }, /* @__PURE__ */ React.createElement("div", { className: "container marq-in" }, t.marquee.map((m, i) => /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, i > 0 && /* @__PURE__ */ React.createElement("b", null, "·"), /* @__PURE__ */ React.createElement("span", null, m))))), /* @__PURE__ */ React.createElement("section", { className: "sec", id: "services" }, /* @__PURE__ */ React.createElement("div", { className: "container" }, /* @__PURE__ */ React.createElement("div", { className: "eyebrow" }, t.nav[0]), /* @__PURE__ */ React.createElement("h2", null, t.servicesTitle), /* @__PURE__ */ React.createElement("p", { className: "sec-lead" }, t.servicesLead), /* @__PURE__ */ React.createElement("div", { className: "svc-grid" }, t.services.map((s, i) => /* @__PURE__ */ React.createElement("article", { className: "svc cs-surface-standard", key: i }, /* @__PURE__ */ React.createElement("div", { className: "svc-ic" }, /* @__PURE__ */ React.createElement(I, { name: s.icon })), /* @__PURE__ */ React.createElement("h3", null, s.t), /* @__PURE__ */ React.createElement("p", null, s.s), /* @__PURE__ */ React.createElement("ul", null, s.o.map((o) => /* @__PURE__ */ React.createElement("li", { key: o }, o)))))))), /* @__PURE__ */ React.createElement("section", { className: "sec sec-alt", id: "process" }, /* @__PURE__ */ React.createElement("div", { className: "container" }, /* @__PURE__ */ React.createElement("div", { className: "eyebrow" }, t.nav[2]), /* @__PURE__ */ React.createElement("h2", null, t.processTitle), /* @__PURE__ */ React.createElement("p", { className: "sec-lead" }, t.processLead), /* @__PURE__ */ React.createElement("div", { className: "steps" }, t.steps.map((s) => /* @__PURE__ */ React.createElement("div", { className: "step", key: s.n }, /* @__PURE__ */ React.createElement("span", { className: "n" }, s.n), /* @__PURE__ */ React.createElement("h3", null, s.t), /* @__PURE__ */ React.createElement("p", null, s.s)))))), /* @__PURE__ */ React.createElement("section", { className: "sec", id: "careers" }, /* @__PURE__ */ React.createElement("div", { className: "container" }, /* @__PURE__ */ React.createElement("div", { className: "band" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", null, t.careersTitle), /* @__PURE__ */ React.createElement("p", null, t.careersLead)), /* @__PURE__ */ React.createElement("button", { className: "btn-gold", onClick: () => openChat("") }, t.careersCta, /* @__PURE__ */ React.createElement(I, { name: "arrow-right", size: 18 }))))), /* @__PURE__ */ React.createElement("section", { className: "sec sec-alt", id: "contact" }, /* @__PURE__ */ React.createElement("div", { className: "container contact-grid" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "eyebrow" }, t.nav[4]), /* @__PURE__ */ React.createElement("h2", null, t.contactTitle), /* @__PURE__ */ React.createElement("p", { className: "sec-lead", style: { marginBottom: 16 } }, t.contactLead), /* @__PURE__ */ React.createElement("p", { className: "trust" }, /* @__PURE__ */ React.createElement(I, { name: "check", size: 16 }), t.form.trust)), sent ? /* @__PURE__ */ React.createElement("div", { className: "sent" }, /* @__PURE__ */ React.createElement(I, { name: "check", size: 18 }), " ", loc === "vi" ? "Đã nhận được lời nhắn. Cảm ơn bạn!" : "Message received. Thank you!") : /* @__PURE__ */ React.createElement("form", { className: "form", onSubmit: (e) => {
|
|
5508
|
-
e.preventDefault();
|
|
5509
|
-
setSent(true);
|
|
5510
|
-
} }, /* @__PURE__ */ React.createElement("label", { className: "cs-field" }, /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, t.form.name), /* @__PURE__ */ React.createElement("input", { className: "cs-field__control", required: true })), /* @__PURE__ */ React.createElement("label", { className: "cs-field" }, /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, t.form.email), /* @__PURE__ */ React.createElement("input", { className: "cs-field__control", type: "email", required: true })), /* @__PURE__ */ React.createElement("label", { className: "cs-field" }, /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, t.form.company, " ", /* @__PURE__ */ React.createElement("span", { className: "muted", style: { fontWeight: 400, color: "var(--cs-color-text-muted)" } }, "· ", t.form.optional)), /* @__PURE__ */ React.createElement("input", { className: "cs-field__control" })), /* @__PURE__ */ React.createElement("label", { className: "cs-field" }, /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, t.form.message), /* @__PURE__ */ React.createElement("textarea", { className: "cs-field__control", rows: "3" })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("button", { type: "submit", className: "btn-gold" }, t.form.submit, /* @__PURE__ */ React.createElement(I, { name: "arrow-right", size: 18 })))))), /* @__PURE__ */ React.createElement("footer", { className: "ft" }, /* @__PURE__ */ React.createElement("div", { className: "container ft-in" }, /* @__PURE__ */ React.createElement("a", { className: "brand", href: "#top" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/logo-mark.svg", alt: "" }), "CyberSkill"), /* @__PURE__ */ React.createElement("div", { className: "ft-links" }, t.footerLinks.map((l) => /* @__PURE__ */ React.createElement("a", { key: l, href: "#top" }, l))), /* @__PURE__ */ React.createElement("small", null, "© 2026 CyberSkill · ", t.footerRights, " · Saigon"))), !chat && /* @__PURE__ */ React.createElement("button", { className: "fab", onClick: () => openChat("") }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/lumi-poster.webp", alt: "" }), t.talk), chat && /* @__PURE__ */ React.createElement(Chat, { t, seed, onClose: () => setChat(false) }));
|
|
5511
|
-
}
|
|
5512
|
-
window.Website = Website;
|
|
5513
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/website/Website.jsx", error: String((e && e.message) || e) }); }
|
|
5514
|
-
|
|
5515
|
-
// ui_kits/website/copy.js
|
|
5516
|
-
try { (() => {
|
|
5517
|
-
window.SITE_COPY = {
|
|
5518
|
-
en: {
|
|
5519
|
-
nav: ["Services", "Work", "Process", "Careers", "Contact"],
|
|
5520
|
-
langLabel: "Tiếng Việt",
|
|
5521
|
-
eyebrow: "A Saigon studio that turns will into working software. Since 2020.",
|
|
5522
|
-
slogan: "Turn Your Will Into Real",
|
|
5523
|
-
sub: "Hiện Thực Hoá Ý Chí",
|
|
5524
|
-
lead: "Lumi, our golden genie, takes a clear wish and helps the human team turn it into software your people actually run — built to ship, and built to last.",
|
|
5525
|
-
wishPlaceholder: "Make a wish… a faster checkout, an app, a tool",
|
|
5526
|
-
wishCta: "Make a wish",
|
|
5527
|
-
talk: "Talk to Lumi",
|
|
5528
|
-
marquee: ["Web apps", "Mobile apps", "Internal systems", "Automation", "Design systems", "Built to last"],
|
|
5529
|
-
servicesTitle: "What we build",
|
|
5530
|
-
servicesLead: "Three focused practices. One team that answers for the result.",
|
|
5531
|
-
services: [
|
|
5532
|
-
{ icon: "sparkle", t: "Web & mobile apps", s: "Product apps your customers reach for — fast, accessible, bilingual by default.", o: ["Web apps", "Mobile apps", "Design systems"] },
|
|
5533
|
-
{ icon: "chat", t: "Internal systems & automation", s: "The tools your team runs on: dashboards, workflows, and the automation behind them.", o: ["Internal systems", "Automation"] },
|
|
5534
|
-
{ icon: "check", t: "Built to last", s: "Shipped software that keeps running — tested, documented, handed over cleanly.", o: ["Testing", "Docs", "Handover"] }
|
|
5535
|
-
],
|
|
5536
|
-
processTitle: "The arc of a wish",
|
|
5537
|
-
processLead: "Every project follows one arc: from a clear wish to software running in the real world.",
|
|
5538
|
-
steps: [
|
|
5539
|
-
{ n: "01", t: "Make a wish", s: "Tell us the outcome in a sentence or two. We reply within one business day." },
|
|
5540
|
-
{ n: "02", t: "Shape it", s: "A small senior team scopes the work and is honest about trade-offs." },
|
|
5541
|
-
{ n: "03", t: "Build to ship", s: "Short cycles, real software you can run — not slideware." },
|
|
5542
|
-
{ n: "04", t: "Built to last", s: "Tested, documented, handed over. It keeps running after we leave." }
|
|
5543
|
-
],
|
|
5544
|
-
careersTitle: "Build with us",
|
|
5545
|
-
careersLead: "We hire for craft and care — senior, small, and honest about trade-offs.",
|
|
5546
|
-
careersCta: "See open roles",
|
|
5547
|
-
contactTitle: "Tell us your wish",
|
|
5548
|
-
contactLead: "Send a short note. We reply within one business day.",
|
|
5549
|
-
form: { name: "Your name", email: "Work email", company: "Company", message: "Anything we should know?", submit: "Send", optional: "optional", trust: "Your details go straight to the team. We never share them." },
|
|
5550
|
-
genie: { title: "Lumi · CyberSkill’s golden genie", greeting: "Hello, I’m Lumi. Think of me as a friendly guide: tell me a wish, and I’ll share plain advice before asking for anything. When a wish is clear, it goes to a real person on the team.", placeholder: "Type your wish…", send: "Send", chips: ["A web app", "An internal tool", "A design system"] },
|
|
5551
|
-
footerRights: "All rights reserved.",
|
|
5552
|
-
footerLinks: ["Privacy", "Accessibility", "Terms"]
|
|
5553
|
-
},
|
|
5554
|
-
vi: {
|
|
5555
|
-
nav: ["Dịch vụ", "Dự án", "Quy trình", "Tuyển dụng", "Liên hệ"],
|
|
5556
|
-
langLabel: "English",
|
|
5557
|
-
eyebrow: "Studio Sài Gòn. Biến ý chí thành phần mềm chạy thật. Từ năm 2020.",
|
|
5558
|
-
slogan: "Hiện Thực Hoá Ý Chí",
|
|
5559
|
-
sub: "Turn Your Will Into Real",
|
|
5560
|
-
lead: "Lumi, thần đèn vàng của CyberSkill, cùng đội ngũ biến điều ước rõ thành phần mềm đội bạn dùng mỗi ngày: bàn giao được, dùng lâu được.",
|
|
5561
|
-
wishPlaceholder: "Ước một điều… thanh toán nhanh hơn, một ứng dụng, một công cụ",
|
|
5562
|
-
wishCta: "Ước điều này",
|
|
5563
|
-
talk: "Trò chuyện với Lumi",
|
|
5564
|
-
marquee: ["Ứng dụng web", "Ứng dụng di động", "Hệ thống nội bộ", "Tự động hóa", "Hệ thống thiết kế", "Bền bỉ lâu dài"],
|
|
5565
|
-
servicesTitle: "Chúng tôi xây dựng những gì",
|
|
5566
|
-
servicesLead: "Ba mảng chuyên sâu. Một đội chịu trách nhiệm đến kết quả cuối cùng.",
|
|
5567
|
-
services: [
|
|
5568
|
-
{ icon: "sparkle", t: "Ứng dụng web & di động", s: "Sản phẩm khách hàng dùng mỗi ngày — nhanh, dễ tiếp cận, song ngữ mặc định.", o: ["Ứng dụng web", "Di động", "Hệ thống thiết kế"] },
|
|
5569
|
-
{ icon: "chat", t: "Hệ thống nội bộ & tự động hóa", s: "Công cụ đội bạn vận hành: bảng điều khiển, quy trình và tự động hóa phía sau.", o: ["Hệ thống nội bộ", "Tự động hóa"] },
|
|
5570
|
-
{ icon: "check", t: "Bền bỉ lâu dài", s: "Phần mềm bàn giao vẫn chạy tốt — được kiểm thử, tài liệu đầy đủ, bàn giao gọn.", o: ["Kiểm thử", "Tài liệu", "Bàn giao"] }
|
|
5571
|
-
],
|
|
5572
|
-
processTitle: "Hành trình của một điều ước",
|
|
5573
|
-
processLead: "Mỗi dự án một hành trình: từ điều ước rõ đến phần mềm chạy giữa đời thật.",
|
|
5574
|
-
steps: [
|
|
5575
|
-
{ n: "01", t: "Ước một điều", s: "Kể kết quả bạn muốn trong một hai câu. Chúng tôi phản hồi trong một ngày làm việc." },
|
|
5576
|
-
{ n: "02", t: "Định hình", s: "Đội nhỏ, kinh nghiệm bóc tách công việc và nói thẳng về đánh đổi." },
|
|
5577
|
-
{ n: "03", t: "Xây để bàn giao", s: "Chu kỳ ngắn, phần mềm chạy thật — không phải slide." },
|
|
5578
|
-
{ n: "04", t: "Dùng lâu được", s: "Kiểm thử, tài liệu, bàn giao. Vẫn chạy tốt sau khi chúng tôi rời đi." }
|
|
5579
|
-
],
|
|
5580
|
-
careersTitle: "Cùng chúng tôi kiến tạo",
|
|
5581
|
-
careersLead: "Tuyển người có tay nghề và chỉn chu — đội gọn, kinh nghiệm, nói thẳng đánh đổi.",
|
|
5582
|
-
careersCta: "Xem vị trí đang mở",
|
|
5583
|
-
contactTitle: "Kể chúng tôi nghe điều bạn ước",
|
|
5584
|
-
contactLead: "Một lời nhắn ngắn là đủ. Chúng tôi phản hồi trong một ngày làm việc.",
|
|
5585
|
-
form: { name: "Tên của bạn", email: "Email công việc", company: "Công ty", message: "Bạn muốn chia sẻ thêm điều gì?", submit: "Gửi", optional: "không bắt buộc", trust: "Thông tin của bạn được gửi thẳng đến đội ngũ. Chúng tôi không chia sẻ cho bên thứ ba." },
|
|
5586
|
-
genie: { title: "Lumi · thần đèn vàng CyberSkill", greeting: "Chào bạn, mình là Lumi. Cứ kể mình một điều ước; mình tư vấn trước, chỉ xin thông tin khi bạn muốn gửi. Điều ước rõ sẽ được chuyển cho người thật trong đội.", placeholder: "Viết điều ước của bạn…", send: "Gửi", chips: ["Một ứng dụng web", "Công cụ nội bộ", "Hệ thống thiết kế"] },
|
|
5587
|
-
footerRights: "Bảo lưu mọi quyền.",
|
|
5588
|
-
footerLinks: ["Quyền riêng tư", "Khả năng tiếp cận", "Điều khoản"]
|
|
5589
|
-
}
|
|
5590
|
-
};
|
|
5591
|
-
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/website/copy.js", error: String((e && e.message) || e) }); }
|
|
5592
|
-
|
|
5593
5353
|
__ds_ns.AIDisclosureBadge = __ds_scope.AIDisclosureBadge;
|
|
5594
5354
|
|
|
5595
5355
|
__ds_ns.ChatMessage = __ds_scope.ChatMessage;
|
|
@@ -5824,6 +5584,8 @@ __ds_ns.Drawer = __ds_scope.Drawer;
|
|
|
5824
5584
|
|
|
5825
5585
|
__ds_ns.HoverCard = __ds_scope.HoverCard;
|
|
5826
5586
|
|
|
5587
|
+
__ds_ns.OverlayProvider = __ds_scope.OverlayProvider;
|
|
5588
|
+
|
|
5827
5589
|
__ds_ns.Popconfirm = __ds_scope.Popconfirm;
|
|
5828
5590
|
|
|
5829
5591
|
__ds_ns.Popover = __ds_scope.Popover;
|