akanjs 3.0.2 → 3.1.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/base/baseEnv.ts +0 -4
- package/base/primitiveRegistry.ts +2 -2
- package/client/cookie.ts +5 -1
- package/client/csrLocation.ts +122 -0
- package/client/csrTypes.ts +32 -4
- package/client/device.ts +2 -1
- package/client/frameConfig.ts +78 -7
- package/client/index.ts +3 -0
- package/client/makePageProto.tsx +3 -2
- package/client/native/external-view.ts +1 -0
- package/client/route/RootLayoutDefinition.ts +7 -1
- package/client/route/RouteHead.ts +108 -0
- package/client/route/index.ts +1 -0
- package/client/router.ts +36 -23
- package/client/rscNavigation.ts +8 -2
- package/client/viewportMeta.ts +60 -0
- package/client/webManifest.ts +18 -0
- package/common/Logger.ts +7 -2
- package/common/PipelineNames.ts +31 -0
- package/common/PipelinePattern.ts +70 -0
- package/common/PipelinePlanSchema.ts +490 -0
- package/common/backoff.ts +14 -0
- package/common/index.ts +39 -0
- package/common/pipelinePlan.ts +259 -0
- package/common/routeConvention.ts +1 -0
- package/common/types.ts +17 -0
- package/constant/constantRegistry.ts +26 -6
- package/dictionary/base.dictionary.ts +8 -0
- package/dictionary/trans.ts +9 -1
- package/document/into.ts +5 -0
- package/fetch/client/fetchClient.ts +135 -31
- package/fetch/client/httpClient.ts +45 -0
- package/fetch/client/wsClient.ts +125 -16
- package/fetch/fetchType/buildFetch.type.ts +1 -0
- package/index.ts +243 -10
- package/package.json +1 -5
- package/server/akanApp.ts +5 -2
- package/server/akanServer.ts +44 -8
- package/server/artifact/builderRpc.ts +8 -3
- package/server/artifact/ipcTypes.ts +5 -2
- package/server/di/diLifecycle.ts +39 -3
- package/server/di/utils.ts +28 -1
- package/server/ed25519Key.ts +48 -0
- package/server/index.ts +1 -0
- package/server/machine/index.ts +2 -0
- package/server/machine/machineAccount.ts +27 -0
- package/server/machine/machineToken.ts +151 -0
- package/server/mcp/McpApps.ts +256 -0
- package/server/mcp/McpDispatcher.ts +31 -7
- package/server/mcp/McpRouter.ts +209 -46
- package/server/ops/opsToken.ts +3 -10
- package/server/resolver/database.resolver.ts +3 -0
- package/server/resolver/signal.resolver.ts +40 -3
- package/server/routeElementComposer.tsx +78 -2
- package/server/routeState.ts +6 -0
- package/server/routeTreeBuilder.ts +26 -9
- package/server/routing/apiRouter.ts +6 -3
- package/server/routing/roomReplay.ts +88 -0
- package/server/rscClient.tsx +53 -8
- package/server/rscWorker.tsx +141 -27
- package/server/rscWorkerHost.ts +18 -5
- package/server/sitemap.ts +14 -0
- package/server/ssrFromRscRenderer.tsx +12 -6
- package/server/ssrTypes.ts +3 -1
- package/server/systemPageDocument.tsx +38 -30
- package/server/types.tsx +2 -0
- package/server/webRouter.ts +74 -5
- package/service/injectInfo.ts +9 -1
- package/service/ipcTypes.ts +2 -1
- package/service/predefinedAdaptor/database.adaptor.ts +1 -146
- package/service/predefinedAdaptor/queue.adaptor.ts +44 -6
- package/service/predefinedAdaptor/queueBackoff.ts +12 -0
- package/service/predefinedAdaptor/solidQueue.adaptor.ts +17 -13
- package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +44 -2
- package/service/predefinedAdaptor/sql/dialect/postgres.ts +3 -0
- package/service/predefinedAdaptor/sql/dialect/sqlite.ts +5 -2
- package/service/predefinedAdaptor/sql/search/fts5.ts +1 -1
- package/service/predefinedAdaptor/sql/types.ts +8 -6
- package/service/predefinedAdaptor/sql/values.ts +0 -23
- package/service/serve.ts +5 -1
- package/signal/agent.signal.ts +1 -1
- package/signal/base.signal.ts +2 -2
- package/signal/endpointIdempotency.ts +53 -0
- package/signal/internalArg.ts +14 -0
- package/signal/mcp/McpDocument.ts +38 -1
- package/signal/mcp/mcpProtocol.ts +18 -0
- package/signal/signalContext.ts +58 -30
- package/signal/signalRegistry.ts +14 -4
- package/signal/types.ts +18 -3
- package/store/agent/StoreSurfaceSource.ts +1 -1
- package/store/agentic/StToolBuilder.ts +3 -0
- package/test/conformance.ts +9 -33
- package/types/base/baseEnv.d.ts +0 -4
- package/types/base/primitiveRegistry.d.ts +1 -1
- package/types/client/csrLocation.d.ts +32 -0
- package/types/client/csrTypes.d.ts +40 -4
- package/types/client/frameConfig.d.ts +14 -2
- package/types/client/index.d.ts +3 -0
- package/types/client/native/external-view.d.ts +1 -0
- package/types/client/route/RootLayoutDefinition.d.ts +2 -1
- package/types/client/route/RouteHead.d.ts +13 -0
- package/types/client/route/index.d.ts +1 -0
- package/types/client/router.d.ts +6 -0
- package/types/client/rscNavigation.d.ts +4 -0
- package/types/client/viewportMeta.d.ts +18 -0
- package/types/client/webManifest.d.ts +4 -0
- package/types/common/PipelineNames.d.ts +6 -0
- package/types/common/PipelinePattern.d.ts +14 -0
- package/types/common/PipelinePlanSchema.d.ts +9 -0
- package/types/common/backoff.d.ts +10 -0
- package/types/common/index.d.ts +5 -0
- package/types/common/pipelinePlan.d.ts +224 -0
- package/types/common/types.d.ts +16 -0
- package/types/constant/constantRegistry.d.ts +5 -2
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictionary.d.ts +8 -8
- package/types/document/into.d.ts +9 -0
- package/types/fetch/client/fetchClient.d.ts +31 -4
- package/types/fetch/client/httpClient.d.ts +8 -0
- package/types/fetch/client/wsClient.d.ts +26 -2
- package/types/fetch/fetchType/buildFetch.type.d.ts +1 -0
- package/types/index.d.ts +230 -9
- package/types/server/akanServer.d.ts +16 -1
- package/types/server/artifact/builderRpc.d.ts +7 -2
- package/types/server/artifact/ipcTypes.d.ts +5 -1
- package/types/server/di/utils.d.ts +1 -0
- package/types/server/ed25519Key.d.ts +13 -0
- package/types/server/index.d.ts +1 -0
- package/types/server/machine/index.d.ts +2 -0
- package/types/server/machine/machineAccount.d.ts +5 -0
- package/types/server/machine/machineToken.d.ts +29 -0
- package/types/server/machine.d.ts +1 -0
- package/types/server/mcp/McpApps.d.ts +50 -0
- package/types/server/mcp/McpDispatcher.d.ts +4 -1
- package/types/server/mcp/McpRouter.d.ts +4 -0
- package/types/server/resolver/signal.resolver.d.ts +2 -1
- package/types/server/routeElementComposer.d.ts +24 -4
- package/types/server/routeState.d.ts +4 -1
- package/types/server/routing/roomReplay.d.ts +24 -0
- package/types/server/rscWorkerHost.d.ts +5 -2
- package/types/server/sitemap.d.ts +2 -0
- package/types/server/ssrFromRscRenderer.d.ts +3 -3
- package/types/server/ssrTypes.d.ts +4 -1
- package/types/server/systemPageDocument.d.ts +2 -0
- package/types/server/types.d.ts +4 -1
- package/types/server/webRouter.d.ts +3 -1
- package/types/service/ipcTypes.d.ts +2 -0
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +1 -24
- package/types/service/predefinedAdaptor/queue.adaptor.d.ts +10 -3
- package/types/service/predefinedAdaptor/queueBackoff.d.ts +4 -0
- package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +3 -3
- package/types/service/predefinedAdaptor/sql/SqlDocumentStore.d.ts +6 -1
- package/types/service/predefinedAdaptor/sql/dialect/postgres.d.ts +1 -0
- package/types/service/predefinedAdaptor/sql/dialect/sqlite.d.ts +1 -0
- package/types/service/predefinedAdaptor/sql/search/fts5.d.ts +1 -1
- package/types/service/predefinedAdaptor/sql/types.d.ts +9 -5
- package/types/service/predefinedAdaptor/sql/values.d.ts +0 -3
- package/types/service/serve.d.ts +3 -0
- package/types/signal/endpointIdempotency.d.ts +7 -0
- package/types/signal/internalArg.d.ts +10 -0
- package/types/signal/mcp/McpDocument.d.ts +8 -0
- package/types/signal/mcp/mcpProtocol.d.ts +17 -0
- package/types/signal/signalContext.d.ts +5 -3
- package/types/signal/signalRegistry.d.ts +4 -4
- package/types/signal/types.d.ts +21 -1
- package/types/store/agentic/StToolBuilder.d.ts +2 -0
- package/types/test/conformance.d.ts +7 -10
- package/types/ui/Agent/Chat.d.ts +5 -1
- package/types/ui/Agent/EmbedApproval.d.ts +1 -0
- package/types/ui/Agent/index.d.ts +3 -0
- package/types/ui/Agent/useKeyboardFrame.d.ts +2 -0
- package/types/ui/Agent/useScreenActions.d.ts +18 -0
- package/types/ui/BottomSheet.d.ts +1 -0
- package/types/ui/Constant/Doc.d.ts +11 -17
- package/types/ui/Constant/index.d.ts +7 -2
- package/types/ui/Constant/schemaDoc.d.ts +11 -0
- package/types/ui/Dialog/Modal.d.ts +6 -1
- package/types/ui/Dialog/index.d.ts +1 -1
- package/types/ui/Dialog/modalSurface.d.ts +12 -0
- package/types/ui/Layout/BottomTab.d.ts +11 -2
- package/types/ui/Layout/FrameSlotFallback.d.ts +9 -0
- package/types/ui/Layout/Navbar.d.ts +4 -1
- package/types/ui/Layout/TopInset.d.ts +1 -1
- package/types/ui/Layout/index.d.ts +3 -3
- package/types/ui/Link/Action.d.ts +5 -1
- package/types/ui/Link/Anchor.d.ts +5 -3
- package/types/ui/Link/activePath.d.ts +13 -0
- package/types/ui/Link/index.d.ts +1 -1
- package/types/ui/Modal.d.ts +6 -1
- package/types/ui/Model/EditModal.d.ts +6 -1
- package/types/ui/Model/New.d.ts +6 -1
- package/types/ui/Model/ViewEditModal.d.ts +6 -1
- package/types/ui/Model/ViewModal.d.ts +6 -1
- package/types/ui/Model/newFormOrigin.d.ts +7 -0
- package/types/ui/Portal.d.ts +3 -1
- package/types/ui/Reference/origin.d.ts +12 -0
- package/types/ui/Signal/Doc.d.ts +23 -18
- package/types/ui/Signal/Listener.d.ts +2 -6
- package/types/ui/Signal/Object.d.ts +6 -10
- package/types/ui/Signal/Response.d.ts +3 -6
- package/types/ui/Signal/RestApi.d.ts +11 -15
- package/types/ui/Signal/WebSocket.d.ts +11 -21
- package/types/ui/Signal/endpointEntries.d.ts +8 -0
- package/types/ui/Signal/index.d.ts +35 -12
- package/types/ui/Signal/signalText.d.ts +178 -0
- package/types/ui/System/CSR.d.ts +2 -2
- package/types/ui/System/Client.d.ts +4 -3
- package/types/ui/System/Common.d.ts +27 -6
- package/types/ui/System/SSR.d.ts +6 -6
- package/types/ui/Tooltip.d.ts +4 -2
- package/types/ui/index.d.ts +2 -1
- package/types/ui/overlayLayer.d.ts +6 -0
- package/types/ui/overlayPosition.d.ts +17 -0
- package/types/vendor/@akanjs/native/packages/cli/src/api.d.ts +14 -3
- package/types/vendor/@akanjs/native/packages/cli/src/commands/update.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/cli/src/config.d.ts +30 -5
- package/types/vendor/@akanjs/native/packages/cli/src/lib/compat.d.ts +8 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.d.ts +5 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/distribution.d.ts +22 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/project.d.ts +4 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/publish.d.ts +16 -10
- package/types/vendor/@akanjs/native/packages/cli/src/lib/updates.d.ts +51 -7
- package/types/vendor/@akanjs/native/packages/cli/src/lib/vectors.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.d.ts +7 -0
- package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows.d.ts +5 -0
- package/types/vendor/@akanjs/native/packages/core/src/kernel.d.ts +36 -0
- package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +2 -0
- package/types/vendor/@akanjs/native/packages/desktop/src/server.d.ts +11 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src/desktop.d.ts +17 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src/index.d.ts +132 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src.d.ts +1 -0
- package/types/vendor/@akanjs/native/plugins/updates/src/index.d.ts +8 -0
- package/types/vendor/@akanjs/native/plugins/window/src/desktop.d.ts +4 -1
- package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +49 -0
- package/types/vendor/use-agentic/types.d.ts +2 -0
- package/types/webkit/CsrStack.d.ts +1 -1
- package/types/webkit/McpAppAgent.d.ts +14 -0
- package/types/webkit/McpAppApprovals.d.ts +8 -0
- package/types/webkit/McpAppBridge.d.ts +101 -0
- package/types/webkit/McpAppView.d.ts +21 -0
- package/types/webkit/index.d.ts +1 -0
- package/types/webkit/useLocation.d.ts +1 -1
- package/types/webkit/useVisualViewportFrame.d.ts +13 -0
- package/ui/Agent/Chat.tsx +29 -10
- package/ui/Agent/Composer.tsx +2 -1
- package/ui/Agent/EmbedApproval.tsx +17 -0
- package/ui/Agent/RichInput.tsx +1 -1
- package/ui/Agent/index.ts +2 -0
- package/ui/Agent/useKeyboardFrame.ts +10 -0
- package/ui/Agent/useScreenActions.ts +45 -0
- package/ui/BottomSheet.tsx +82 -53
- package/ui/Constant/Doc.tsx +113 -60
- package/ui/Constant/index.ts +2 -2
- package/ui/Constant/schemaDoc.ts +86 -26
- package/ui/Dialog/LegacyModal.tsx +53 -52
- package/ui/Dialog/Modal.tsx +93 -49
- package/ui/Dialog/modalSurface.ts +61 -0
- package/ui/DragAction.tsx +1 -1
- package/ui/DraggableList.tsx +1 -1
- package/ui/Dropdown.tsx +4 -6
- package/ui/KeyboardAvoiding.tsx +1 -1
- package/ui/Layout/BottomInset.tsx +20 -14
- package/ui/Layout/BottomTab.tsx +42 -16
- package/ui/Layout/FrameSlotFallback.tsx +59 -0
- package/ui/Layout/Navbar.tsx +37 -11
- package/ui/Layout/Sider.tsx +1 -1
- package/ui/Layout/TopInset.tsx +15 -14
- package/ui/Link/Action.tsx +15 -5
- package/ui/Link/Anchor.tsx +25 -13
- package/ui/Link/activePath.ts +25 -0
- package/ui/Link/index.tsx +1 -1
- package/ui/Load/Pagination.tsx +1 -1
- package/ui/Load/Units.tsx +1 -1
- package/ui/Loading/ProgressBar.tsx +1 -1
- package/ui/Modal.tsx +9 -0
- package/ui/Model/EditModal.tsx +16 -1
- package/ui/Model/New.tsx +25 -2
- package/ui/Model/NewWrapper_Client.tsx +2 -0
- package/ui/Model/ViewEditModal.tsx +9 -0
- package/ui/Model/ViewModal.tsx +16 -1
- package/ui/Model/newFormOrigin.ts +45 -0
- package/ui/Popconfirm.tsx +5 -7
- package/ui/Portal.tsx +4 -7
- package/ui/Reference/origin.ts +31 -0
- package/ui/ScreenNavigator.tsx +1 -1
- package/ui/Select.tsx +2 -7
- package/ui/Signal/Arg.tsx +2 -2
- package/ui/Signal/Doc.tsx +125 -63
- package/ui/Signal/Endpoint.tsx +11 -10
- package/ui/Signal/Listener.tsx +7 -10
- package/ui/Signal/Object.tsx +7 -14
- package/ui/Signal/Response.tsx +24 -24
- package/ui/Signal/RestApi.tsx +63 -52
- package/ui/Signal/WebSocket.tsx +65 -72
- package/ui/Signal/endpointEntries.ts +25 -0
- package/ui/Signal/index.ts +23 -6
- package/ui/Signal/signalText.ts +59 -0
- package/ui/System/CSR.tsx +34 -8
- package/ui/System/Client.tsx +36 -5
- package/ui/System/Common.tsx +76 -32
- package/ui/System/SSR.tsx +73 -31
- package/ui/Toast.tsx +1 -1
- package/ui/Tooltip.tsx +84 -30
- package/ui/index.ts +11 -1
- package/ui/overlayLayer.ts +25 -2
- package/ui/overlayPosition.ts +90 -0
- package/ui/recipe/inputRecipe.ts +8 -1
- package/ui/styles.css +152 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +22 -2
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeKernel.kt +37 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdateReports.kt +169 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdates.kt +110 -38
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeVectors.kt +20 -0
- package/vendor/@akanjs/native/native/desktop/Cargo.lock +16 -16
- package/vendor/@akanjs/native/native/desktop/src/external.rs +651 -0
- package/vendor/@akanjs/native/native/desktop/src/json.rs +23 -0
- package/vendor/@akanjs/native/native/desktop/src/lib.rs +228 -5
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeApp.swift +2 -1
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeKernel.swift +47 -1
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdateReports.swift +145 -0
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdates.swift +112 -43
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeVectors.swift +18 -0
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeViewController.swift +24 -2
- package/vendor/@akanjs/native/packages/cli/src/api.ts +22 -6
- package/vendor/@akanjs/native/packages/cli/src/commands/build.ts +24 -8
- package/vendor/@akanjs/native/packages/cli/src/commands/update.ts +11 -9
- package/vendor/@akanjs/native/packages/cli/src/config.ts +35 -7
- package/vendor/@akanjs/native/packages/cli/src/lib/compat.ts +2 -0
- package/vendor/@akanjs/native/packages/cli/src/lib/configkeys.ts +6 -4
- package/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.ts +11 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/distribution.ts +73 -0
- package/vendor/@akanjs/native/packages/cli/src/lib/project.ts +15 -3
- package/vendor/@akanjs/native/packages/cli/src/lib/publish.ts +70 -23
- package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +159 -23
- package/vendor/@akanjs/native/packages/cli/src/lib/vectors.ts +2 -0
- package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +9 -4
- package/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/platforms/desktop.ts +4 -2
- package/vendor/@akanjs/native/packages/cli/src/platforms/ios.ts +9 -4
- package/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.ts +25 -11
- package/vendor/@akanjs/native/packages/cli/src/platforms/windows.ts +14 -3
- package/vendor/@akanjs/native/packages/core/src/kernel.ts +49 -0
- package/vendor/@akanjs/native/packages/core/vectors/updates.json +31 -0
- package/vendor/@akanjs/native/packages/desktop/src/main.ts +3 -0
- package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +2 -0
- package/vendor/@akanjs/native/packages/desktop/src/server.ts +70 -6
- package/vendor/@akanjs/native/plugins/external-view/native-plugin.json +23 -0
- package/vendor/@akanjs/native/plugins/external-view/src/desktop.ts +321 -0
- package/vendor/@akanjs/native/plugins/external-view/src/index.ts +106 -0
- package/vendor/@akanjs/native/plugins/updates/android/UpdatesPlugin.kt +133 -41
- package/vendor/@akanjs/native/plugins/updates/ios/UpdatesPlugin.swift +101 -36
- package/vendor/@akanjs/native/plugins/updates/native-plugin.json +4 -2
- package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +15 -1
- package/vendor/@akanjs/native/plugins/updates/src/index.ts +7 -1
- package/vendor/@akanjs/native/plugins/window/native-plugin.json +4 -0
- package/vendor/@akanjs/native/plugins/window/src/desktop.ts +68 -1
- package/vendor/@akanjs/native/plugins/window/src/index.ts +47 -0
- package/vendor/use-agentic/ToolRunner.ts +8 -1
- package/vendor/use-agentic/types.ts +2 -0
- package/webkit/CsrRouteTable.ts +1 -0
- package/webkit/CsrStack.ts +9 -2
- package/webkit/McpAppAgent.ts +138 -0
- package/webkit/McpAppApprovals.ts +56 -0
- package/webkit/McpAppBridge.ts +291 -0
- package/webkit/McpAppView.ts +258 -0
- package/webkit/bootCsr.tsx +54 -5
- package/webkit/index.ts +1 -0
- package/webkit/useCsrValues.ts +16 -10
- package/webkit/useFrameRuntime.ts +10 -1
- package/webkit/useLocation.ts +4 -3
- package/webkit/useVisualViewportFrame.tsx +52 -0
- package/service/predefinedAdaptor/sql/driver/libsql.ts +0 -45
- package/types/service/predefinedAdaptor/sql/driver/libsql.d.ts +0 -20
- package/types/ui/Agent/useKeyboardInset.d.ts +0 -1
- package/types/ui/ServerPortal.d.ts +0 -15
- package/ui/Agent/useKeyboardInset.ts +0 -19
- package/ui/ServerPortal.tsx +0 -49
package/ui/Layout/BottomTab.tsx
CHANGED
|
@@ -1,32 +1,46 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn, usePage } from "akanjs/client";
|
|
2
|
+
import { cn, usePage, usePathCtx } from "akanjs/client";
|
|
3
3
|
import { st } from "akanjs/store";
|
|
4
4
|
import type { ReactNode } from "react";
|
|
5
5
|
|
|
6
6
|
import { Link } from "../Link";
|
|
7
|
+
import { isActiveHref } from "../Link/activePath";
|
|
7
8
|
import { BottomInset } from "./BottomInset";
|
|
8
9
|
|
|
9
10
|
interface TabType {
|
|
10
11
|
name: ReactNode;
|
|
11
|
-
icon
|
|
12
|
+
icon?: ReactNode;
|
|
12
13
|
activeIcon?: ReactNode;
|
|
14
|
+
/** Drawn in the tab's link in place of icon and name, and passable from a server component, unlike `renderTab`. */
|
|
15
|
+
content?: ReactNode;
|
|
16
|
+
activeContent?: ReactNode;
|
|
13
17
|
notiCount?: number;
|
|
14
18
|
href: string;
|
|
19
|
+
/** Lights on `href` alone, not on the pages under it: a home tab at `/org/<id>` under which every org page sits. */
|
|
20
|
+
exact?: boolean;
|
|
21
|
+
/** More paths that light the tab, each with the pages under it: a "More" tab that claims several sections. */
|
|
22
|
+
match?: string[];
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
export interface BottomTabProps {
|
|
18
26
|
className?: string;
|
|
19
27
|
tabs: TabType[];
|
|
20
28
|
height?: number;
|
|
29
|
+
/** Names the tab bar's navigation landmark for a screen reader. */
|
|
30
|
+
label?: string;
|
|
21
31
|
/** Draws one tab inside its link. The link, the route match, and the badge placement stay the framework's. */
|
|
22
32
|
renderTab?: (tab: TabType, active: boolean) => ReactNode;
|
|
23
33
|
}
|
|
24
34
|
|
|
25
|
-
export const BottomTab = ({ className, tabs, height = 64, renderTab }: BottomTabProps) => {
|
|
35
|
+
export const BottomTab = ({ className, tabs, height = 64, label, renderTab }: BottomTabProps) => {
|
|
26
36
|
const { lang } = usePage();
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
37
|
+
const pathCtx = usePathCtx();
|
|
38
|
+
const statePathname = st.use.pathname({ agent: false });
|
|
39
|
+
|
|
40
|
+
const pathname = typeof window === "undefined" ? (pathCtx.location?.pathname ?? statePathname) : statePathname;
|
|
41
|
+
const prefix = pathCtx.prefix;
|
|
42
|
+
const isActiveTab = ({ href, exact, match }: TabType) =>
|
|
43
|
+
isActiveHref({ pathname, href, lang, prefix: prefix ?? "", exact, match });
|
|
30
44
|
|
|
31
45
|
return (
|
|
32
46
|
<BottomInset
|
|
@@ -37,35 +51,47 @@ export const BottomTab = ({ className, tabs, height = 64, renderTab }: BottomTab
|
|
|
37
51
|
frameScope="layout"
|
|
38
52
|
frameSource="bottomTab"
|
|
39
53
|
>
|
|
40
|
-
<
|
|
54
|
+
<nav
|
|
55
|
+
aria-label={label}
|
|
41
56
|
className={cn(
|
|
42
57
|
"flex size-full items-center justify-around border-border border-t bg-background/95 text-foreground backdrop-blur-sm",
|
|
43
58
|
className,
|
|
44
59
|
)}
|
|
45
60
|
>
|
|
46
61
|
{tabs.map((tab) => {
|
|
47
|
-
const active = isActiveTab(tab
|
|
62
|
+
const active = isActiveTab(tab);
|
|
63
|
+
const custom = renderTab ? renderTab(tab, active) : active ? (tab.activeContent ?? tab.content) : tab.content;
|
|
64
|
+
const badge = tab.notiCount ? (
|
|
65
|
+
<span
|
|
66
|
+
className={cn(
|
|
67
|
+
"absolute flex h-4 min-w-4 items-center justify-center rounded-full bg-destructive px-1 font-medium text-[10px] text-destructive-foreground leading-none",
|
|
68
|
+
custom !== undefined ? "top-1 left-[calc(50%+0.5rem)]" : "-top-1 -right-2",
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
{tab.notiCount > 99 ? "99+" : tab.notiCount}
|
|
72
|
+
</span>
|
|
73
|
+
) : null;
|
|
48
74
|
return (
|
|
49
75
|
<Link
|
|
50
76
|
key={tab.href}
|
|
51
77
|
href={tab.href}
|
|
52
78
|
replace
|
|
79
|
+
aria-current={active ? "page" : undefined}
|
|
53
80
|
className={cn(
|
|
54
81
|
"relative flex w-full flex-col items-center justify-end gap-1 py-1.5 transition-colors",
|
|
55
82
|
active ? "font-medium text-primary" : "text-foreground/55 hover:text-foreground/80",
|
|
56
83
|
)}
|
|
57
84
|
>
|
|
58
|
-
{
|
|
59
|
-
|
|
85
|
+
{custom !== undefined ? (
|
|
86
|
+
<>
|
|
87
|
+
{custom}
|
|
88
|
+
{badge}
|
|
89
|
+
</>
|
|
60
90
|
) : (
|
|
61
91
|
<>
|
|
62
92
|
<div className="relative inline-flex w-max text-xl">
|
|
63
93
|
{active ? (tab.activeIcon ?? tab.icon) : tab.icon}
|
|
64
|
-
{
|
|
65
|
-
<span className="absolute -top-1 -right-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-destructive px-1 font-medium text-[10px] text-destructive-foreground leading-none">
|
|
66
|
-
{tab.notiCount > 99 ? "99+" : tab.notiCount}
|
|
67
|
-
</span>
|
|
68
|
-
) : null}
|
|
94
|
+
{badge}
|
|
69
95
|
</div>
|
|
70
96
|
<span className="text-[11px]">{tab.name}</span>
|
|
71
97
|
</>
|
|
@@ -73,7 +99,7 @@ export const BottomTab = ({ className, tabs, height = 64, renderTab }: BottomTab
|
|
|
73
99
|
</Link>
|
|
74
100
|
);
|
|
75
101
|
})}
|
|
76
|
-
</
|
|
102
|
+
</nav>
|
|
77
103
|
</BottomInset>
|
|
78
104
|
);
|
|
79
105
|
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { getEnv } from "akanjs/base";
|
|
2
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
export type FrameSlotName = "topInset" | "topLeftAction" | "bottomInset" | "keyboardInset";
|
|
5
|
+
|
|
6
|
+
const topVisibility = "var(--akan-top-inset-visibility, visible)" as CSSProperties["visibility"];
|
|
7
|
+
const bottomVisibility = "var(--akan-bottom-inset-visibility, visible)" as CSSProperties["visibility"];
|
|
8
|
+
const top: CSSProperties = {
|
|
9
|
+
top: "calc(var(--akan-top-safe-area) + var(--akan-viewport-offset-top, 0px))",
|
|
10
|
+
height: "var(--akan-top-inset)",
|
|
11
|
+
visibility: topVisibility,
|
|
12
|
+
};
|
|
13
|
+
const bottom: CSSProperties = {
|
|
14
|
+
bottom: "var(--akan-bottom-safe-area)",
|
|
15
|
+
height: "var(--akan-bottom-inset)",
|
|
16
|
+
visibility: bottomVisibility,
|
|
17
|
+
};
|
|
18
|
+
const keyboard: CSSProperties = {
|
|
19
|
+
bottom: "max(var(--akan-bottom-safe-area), var(--akan-keyboard-inset, 0px))",
|
|
20
|
+
height: "var(--akan-bottom-inset)",
|
|
21
|
+
visibility: bottomVisibility,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const outerClassNames: { [slot in FrameSlotName]: string } = {
|
|
25
|
+
topInset: "fixed inset-x-0 z-10 isolate overflow-hidden bg-background",
|
|
26
|
+
topLeftAction: "fixed left-0 z-10 isolate flex aspect-1 items-center justify-center overflow-hidden",
|
|
27
|
+
bottomInset: "pointer-events-none fixed inset-x-0 z-10 isolate overflow-hidden",
|
|
28
|
+
keyboardInset: "pointer-events-none fixed inset-x-0 z-10 isolate overflow-hidden",
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
const innerClassNames: { [slot in FrameSlotName]: string } = {
|
|
32
|
+
topInset: "relative isolate size-full",
|
|
33
|
+
topLeftAction: "isolate flex size-full items-center justify-center",
|
|
34
|
+
bottomInset: "pointer-events-none isolate size-full",
|
|
35
|
+
keyboardInset: "pointer-events-none isolate size-full",
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
const slotStyles: { [slot in FrameSlotName]: CSSProperties } = {
|
|
39
|
+
topInset: top,
|
|
40
|
+
topLeftAction: top,
|
|
41
|
+
bottomInset: bottom,
|
|
42
|
+
keyboardInset: keyboard,
|
|
43
|
+
} as const;
|
|
44
|
+
|
|
45
|
+
interface FrameSlotFallbackProps {
|
|
46
|
+
slot: FrameSlotName;
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Chrome drawn in place on the server, in the box its frame slot draws, until the client moves it into that slot. */
|
|
51
|
+
export const FrameSlotFallback = ({ slot, children }: FrameSlotFallbackProps) => {
|
|
52
|
+
|
|
53
|
+
if (getEnv().renderMode === "csr") return null;
|
|
54
|
+
return (
|
|
55
|
+
<div data-akan-frame-fallback={slot} className={outerClassNames[slot]} style={slotStyles[slot]}>
|
|
56
|
+
<div className={innerClassNames[slot]}>{children}</div>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
};
|
package/ui/Layout/Navbar.tsx
CHANGED
|
@@ -6,20 +6,33 @@ import { BiChevronLeft, BiX } from "react-icons/bi";
|
|
|
6
6
|
|
|
7
7
|
import { Link } from "../Link";
|
|
8
8
|
import { Portal } from "../Portal";
|
|
9
|
+
import { FrameSlotFallback } from "./FrameSlotFallback";
|
|
9
10
|
|
|
10
11
|
export interface NavbarProps {
|
|
11
12
|
className?: string;
|
|
12
13
|
children?: ReactNode;
|
|
13
14
|
height?: number;
|
|
14
15
|
title?: ReactNode;
|
|
16
|
+
/** `true` draws the framework's back button; any other node is drawn in its place. */
|
|
15
17
|
back?: ReactNode;
|
|
18
|
+
/** Where the default back button goes when there is no history to go back to, as on a deep link. */
|
|
19
|
+
backFallback?: string;
|
|
16
20
|
left?: ReactNode;
|
|
17
21
|
right?: ReactNode;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
const closeIconTransitions = new Set<TransitionType>(["bottomUp", "scaleOut", "fade"]);
|
|
21
25
|
|
|
22
|
-
export const Navbar = ({
|
|
26
|
+
export const Navbar = ({
|
|
27
|
+
back = false,
|
|
28
|
+
backFallback,
|
|
29
|
+
className,
|
|
30
|
+
height,
|
|
31
|
+
children,
|
|
32
|
+
title,
|
|
33
|
+
left,
|
|
34
|
+
right,
|
|
35
|
+
}: NavbarProps) => {
|
|
23
36
|
const pathCtx = usePathCtx();
|
|
24
37
|
const pathRoute = pathCtx.location?.pathRoute;
|
|
25
38
|
const path = pathRoute?.path;
|
|
@@ -41,20 +54,33 @@ export const Navbar = ({ back = false, className, height, children, title, left,
|
|
|
41
54
|
height,
|
|
42
55
|
});
|
|
43
56
|
}, [registerFrameSlot, height, path]);
|
|
57
|
+
const bar = (
|
|
58
|
+
<div className={cn("flex size-full items-center gap-2 px-5", back && "pl-14", className)}>
|
|
59
|
+
{left ? <div className="flex shrink-0 items-center gap-1">{left}</div> : null}
|
|
60
|
+
{title ? <div className="min-w-0 flex-1 truncate font-semibold text-base">{title}</div> : null}
|
|
61
|
+
{children}
|
|
62
|
+
{right ? <div className="ml-auto flex shrink-0 items-center gap-1">{right}</div> : null}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
const backButton =
|
|
66
|
+
back === true ? (
|
|
67
|
+
<Link.Back className="text-4xl" fallback={backFallback}>
|
|
68
|
+
<BackIcon />
|
|
69
|
+
</Link.Back>
|
|
70
|
+
) : (
|
|
71
|
+
back
|
|
72
|
+
);
|
|
44
73
|
return (
|
|
45
74
|
<>
|
|
46
|
-
<Portal id={`topInsetContent${suffix}`}>
|
|
47
|
-
|
|
75
|
+
<Portal id={`topInsetContent${suffix}`} fallback={<FrameSlotFallback slot="topInset">{bar}</FrameSlotFallback>}>
|
|
76
|
+
{bar}
|
|
48
77
|
</Portal>
|
|
49
78
|
{back ? (
|
|
50
|
-
<Portal
|
|
51
|
-
{
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
) : (
|
|
56
|
-
back
|
|
57
|
-
)}
|
|
79
|
+
<Portal
|
|
80
|
+
id={`topLeftActionContent${suffix}`}
|
|
81
|
+
fallback={<FrameSlotFallback slot="topLeftAction">{backButton}</FrameSlotFallback>}
|
|
82
|
+
>
|
|
83
|
+
{backButton}
|
|
58
84
|
</Portal>
|
|
59
85
|
) : null}
|
|
60
86
|
</>
|
package/ui/Layout/Sider.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "akanjs/client";
|
|
3
3
|
import { st } from "akanjs/store";
|
|
4
|
-
import { animated } from "akanjs/ui";
|
|
5
4
|
import { type ReactNode, useEffect, useState } from "react";
|
|
6
5
|
import { AiOutlineClose, AiOutlineMenu } from "react-icons/ai";
|
|
7
6
|
import { BiX } from "react-icons/bi";
|
|
8
7
|
import { useSpring } from "react-spring";
|
|
9
8
|
import { agentAttrs } from "../agentAttrs";
|
|
9
|
+
import { animated } from "../animated";
|
|
10
10
|
import { buttonRecipe } from "../Button";
|
|
11
11
|
|
|
12
12
|
export interface SiderProps {
|
package/ui/Layout/TopInset.tsx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { getEnv } from "akanjs/base";
|
|
3
3
|
import { cn, DEFAULT_TOP_INSET, debugFrame, usePathCtx } from "akanjs/client";
|
|
4
|
-
import { type ReactNode,
|
|
4
|
+
import { type ReactNode, useLayoutEffect } from "react";
|
|
5
5
|
|
|
6
6
|
import { Portal } from "../Portal";
|
|
7
|
+
import { FrameSlotFallback } from "./FrameSlotFallback";
|
|
7
8
|
|
|
8
9
|
export interface TopInsetProps {
|
|
9
10
|
className?: string;
|
|
@@ -32,11 +33,14 @@ export const TopInset = ({ className, children, estimatedHeight = DEFAULT_TOP_IN
|
|
|
32
33
|
});
|
|
33
34
|
}, [registerFrameSlot, estimatedHeight, path]);
|
|
34
35
|
|
|
36
|
+
const content = (
|
|
37
|
+
<div data-akan-frame-slot="topInset" data-akan-frame-role="topChrome" className={cn("size-full", className)}>
|
|
38
|
+
{children}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
35
41
|
return (
|
|
36
|
-
<Portal id={`topInsetContent${suffix}`}>
|
|
37
|
-
|
|
38
|
-
{children}
|
|
39
|
-
</div>
|
|
42
|
+
<Portal id={`topInsetContent${suffix}`} fallback={<FrameSlotFallback slot="topInset">{content}</FrameSlotFallback>}>
|
|
43
|
+
{content}
|
|
40
44
|
</Portal>
|
|
41
45
|
);
|
|
42
46
|
};
|
|
@@ -47,19 +51,16 @@ export interface TopLeftActionProps {
|
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
export const TopLeftAction = ({ className, children }: TopLeftActionProps) => {
|
|
50
|
-
const [render, setRender] = useState(false);
|
|
51
54
|
const pathCtx = usePathCtx();
|
|
52
55
|
const path = pathCtx.location?.pathRoute?.path;
|
|
53
56
|
const suffix = getEnv().renderMode === "csr" && path ? `-${pathCtx.pageKey ?? path}` : "";
|
|
54
|
-
|
|
55
|
-
setRender(true);
|
|
56
|
-
}, []);
|
|
57
|
-
|
|
58
|
-
if (!render) return null;
|
|
59
|
-
|
|
57
|
+
const content = <div className={className}>{children}</div>;
|
|
60
58
|
return (
|
|
61
|
-
<Portal
|
|
62
|
-
|
|
59
|
+
<Portal
|
|
60
|
+
id={`topLeftActionContent${suffix}`}
|
|
61
|
+
fallback={<FrameSlotFallback slot="topLeftAction">{content}</FrameSlotFallback>}
|
|
62
|
+
>
|
|
63
|
+
{content}
|
|
63
64
|
</Portal>
|
|
64
65
|
);
|
|
65
66
|
};
|
package/ui/Link/Action.tsx
CHANGED
|
@@ -7,11 +7,21 @@ export interface ActionProps {
|
|
|
7
7
|
children?: ReactNode;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
export interface BackProps extends ActionProps {
|
|
11
|
+
/** Where to go when there is no history to go back to, as on a deep link or a freshly opened app. */
|
|
12
|
+
fallback?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Back = ({ className, children, fallback }: BackProps) =>
|
|
16
|
+
fallback === undefined ? (
|
|
17
|
+
<div className={cn("cursor-pointer", className)} onClick={() => router.back()}>
|
|
18
|
+
{children}
|
|
19
|
+
</div>
|
|
20
|
+
) : (
|
|
21
|
+
<button type="button" className={cn("cursor-pointer", className)} onClick={() => router.backOrFallback(fallback)}>
|
|
22
|
+
{children}
|
|
23
|
+
</button>
|
|
24
|
+
);
|
|
15
25
|
|
|
16
26
|
export const Close = ({ className, children }: ActionProps) => (
|
|
17
27
|
<div className={cn("cursor-pointer", className)} onClick={() => window.close()}>
|
package/ui/Link/Anchor.tsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { getEnv } from "akanjs/base";
|
|
3
|
-
import { cn, getPathInfo, router, usePage, usePathCtx } from "akanjs/client";
|
|
3
|
+
import { cn, csrLocation, getPathInfo, router, usePage, usePathCtx } from "akanjs/client";
|
|
4
4
|
import { openExternalUrl } from "akanjs/client/native";
|
|
5
5
|
import { Logger } from "akanjs/common";
|
|
6
6
|
import { st } from "akanjs/store";
|
|
7
7
|
import type { AnchorHTMLAttributes } from "react";
|
|
8
|
+
import { isActiveHref } from "./activePath";
|
|
8
9
|
|
|
9
10
|
export type CommonLinkProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> & {
|
|
10
11
|
/** Omitted, or with `disabled`, Link renders a plain div around the same children. */
|
|
@@ -13,9 +14,11 @@ export type CommonLinkProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "hre
|
|
|
13
14
|
scrollToTop?: boolean;
|
|
14
15
|
/** Replaces the current history entry instead of pushing a new one. */
|
|
15
16
|
replace?: boolean;
|
|
16
|
-
/** Applied while the current
|
|
17
|
+
/** Applied while the current page is `href` or below it, or exactly `href` with `activeExact`; basePath-aware. */
|
|
17
18
|
activeClassName?: string;
|
|
18
19
|
activeExact?: boolean;
|
|
20
|
+
/** More paths under which the link counts as active, as a tab does for its detail pages. */
|
|
21
|
+
activeMatch?: string[];
|
|
19
22
|
/** @deprecated Has no effect: neither renderer reads it. */
|
|
20
23
|
noCache?: boolean;
|
|
21
24
|
};
|
|
@@ -29,26 +32,26 @@ export const CsrLink = ({
|
|
|
29
32
|
activeClassName,
|
|
30
33
|
scrollToTop,
|
|
31
34
|
activeExact,
|
|
35
|
+
activeMatch,
|
|
32
36
|
noCache,
|
|
33
37
|
...props
|
|
34
38
|
}: AnchorProps) => {
|
|
35
39
|
const { prefix } = usePathCtx();
|
|
36
|
-
const
|
|
40
|
+
const pathname = st.use.pathname({ agent: false });
|
|
37
41
|
const { lang } = usePage();
|
|
38
|
-
const {
|
|
42
|
+
const { hash } = getPathInfo(href, lang, prefix ?? "");
|
|
43
|
+
const active =
|
|
44
|
+
!!activeClassName &&
|
|
45
|
+
isActiveHref({ pathname, href, lang, prefix: prefix ?? "", exact: activeExact, match: activeMatch });
|
|
39
46
|
return (
|
|
40
47
|
<a
|
|
41
|
-
className={cn(
|
|
42
|
-
"cursor-pointer",
|
|
43
|
-
className,
|
|
44
|
-
(activeExact ? currentPath === path : currentPath.startsWith(path)) && activeClassName,
|
|
45
|
-
)}
|
|
48
|
+
className={cn("cursor-pointer", className, active && activeClassName)}
|
|
46
49
|
{...props}
|
|
47
50
|
onClick={(event) => {
|
|
48
51
|
props.onClick?.(event);
|
|
49
52
|
if (event.defaultPrevented) return;
|
|
50
53
|
const isExternal = href.startsWith("http") || href.startsWith("mailto:") || href.startsWith("tel:");
|
|
51
|
-
const url = href.startsWith("#") ? `${
|
|
54
|
+
const url = href.startsWith("#") ? `${csrLocation.pathname}#${hash}` : href;
|
|
52
55
|
if (isExternal) void openExternalUrl(href).catch(() => window.open(href, "_blank", "noopener,noreferrer"));
|
|
53
56
|
else if (replace) router.replace(url, { scrollToTop });
|
|
54
57
|
else router.push(url, { scrollToTop });
|
|
@@ -67,13 +70,17 @@ export const SsrLink = ({
|
|
|
67
70
|
replace,
|
|
68
71
|
activeClassName,
|
|
69
72
|
activeExact,
|
|
73
|
+
activeMatch,
|
|
70
74
|
noCache,
|
|
71
75
|
...props
|
|
72
76
|
}: AnchorProps) => {
|
|
73
77
|
const pathCtx = usePathCtx();
|
|
74
78
|
const prefix = pathCtx.prefix ?? "";
|
|
75
|
-
const { lang
|
|
76
|
-
const
|
|
79
|
+
const { lang } = usePage();
|
|
80
|
+
const statePathname = st.use.pathname({ agent: false });
|
|
81
|
+
|
|
82
|
+
const pathname = typeof window === "undefined" ? (pathCtx.location?.pathname ?? statePathname) : statePathname;
|
|
83
|
+
const currentPath = getPathInfo(pathname, lang, prefix).path;
|
|
77
84
|
const isExternal = href.startsWith("http") || href.startsWith("mailto:") || href.startsWith("tel:");
|
|
78
85
|
const internalPathInfo = getPathInfo(href, lang, prefix);
|
|
79
86
|
const requestHref = getEnv().operationMode === "local" ? internalPathInfo.href : getPathInfo(href, lang, "").href;
|
|
@@ -86,7 +93,12 @@ export const SsrLink = ({
|
|
|
86
93
|
);
|
|
87
94
|
return (
|
|
88
95
|
<a
|
|
89
|
-
className={cn(
|
|
96
|
+
className={cn(
|
|
97
|
+
className,
|
|
98
|
+
!!activeClassName &&
|
|
99
|
+
isActiveHref({ pathname, href, lang, prefix, exact: activeExact, match: activeMatch }) &&
|
|
100
|
+
activeClassName,
|
|
101
|
+
)}
|
|
90
102
|
href={isExternal ? href : requestHref}
|
|
91
103
|
{...props}
|
|
92
104
|
onClick={(event) => {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getPathInfo } from "akanjs/client";
|
|
2
|
+
|
|
3
|
+
interface ActiveHrefOptions {
|
|
4
|
+
/** The current location's pathname, locale and basePath included or not. */
|
|
5
|
+
pathname: string;
|
|
6
|
+
href: string;
|
|
7
|
+
lang: string;
|
|
8
|
+
prefix: string;
|
|
9
|
+
exact?: boolean;
|
|
10
|
+
/** More paths that light the link, each matched as a prefix like `href`. */
|
|
11
|
+
match?: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const pathOf = (url: string, lang: string, prefix: string) => getPathInfo(url, lang, prefix).path || "/";
|
|
15
|
+
|
|
16
|
+
const covers = (current: string, target: string, exact: boolean) =>
|
|
17
|
+
exact || target === "/"
|
|
18
|
+
? current === target
|
|
19
|
+
: current === target || current.startsWith(`${target.replace(/\/$/, "")}/`);
|
|
20
|
+
|
|
21
|
+
/** Whether a link to `href` is the current page, with the locale and the basePath stripped from both sides. */
|
|
22
|
+
export const isActiveHref = ({ pathname, href, lang, prefix, exact = false, match = [] }: ActiveHrefOptions) => {
|
|
23
|
+
const current = pathOf(pathname, lang, prefix);
|
|
24
|
+
return [href, ...match].some((target, idx) => covers(current, pathOf(target, lang, prefix), idx === 0 && exact));
|
|
25
|
+
};
|
package/ui/Link/index.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { type CommonLinkProps, CsrLink, SsrLink } from "./Anchor";
|
|
|
6
6
|
|
|
7
7
|
export const Link = ({ className, href, disabled = false, children, ...props }: CommonLinkProps) => {
|
|
8
8
|
if (disabled || !href) {
|
|
9
|
-
const { scrollToTop, replace, activeClassName, activeExact, noCache, ...divProps } = props;
|
|
9
|
+
const { scrollToTop, replace, activeClassName, activeExact, activeMatch, noCache, ...divProps } = props;
|
|
10
10
|
return (
|
|
11
11
|
<div className={className} {...(divProps as unknown as HTMLAttributes<HTMLDivElement>)}>
|
|
12
12
|
{children}
|
package/ui/Load/Pagination.tsx
CHANGED
|
@@ -54,7 +54,7 @@ function Render<RefName extends string, Light>({ className, init, scrollToTop }:
|
|
|
54
54
|
onSelect: (page) => {
|
|
55
55
|
void storeDo[namesOfSlice.setPageOfModel](page);
|
|
56
56
|
if (!scrollToTop) return;
|
|
57
|
-
window.parent.postMessage({ type: "pathChange", page }, "*");
|
|
57
|
+
if (!globalThis.__AKAN_EMBED__) window.parent.postMessage({ type: "pathChange", page }, "*");
|
|
58
58
|
window.scrollTo({ top: 0, behavior: "instant" });
|
|
59
59
|
},
|
|
60
60
|
});
|
package/ui/Load/Units.tsx
CHANGED
|
@@ -168,7 +168,7 @@ function Render<RefName extends string, Light extends { id: string }>({
|
|
|
168
168
|
onPageSelect: (page: number, option?: { scrollToTop?: boolean }) => {
|
|
169
169
|
void storeDo[namesOfSlice.setPageOfModel](page);
|
|
170
170
|
if (option?.scrollToTop !== false) {
|
|
171
|
-
window.parent.postMessage({ type: "pathChange", page }, "*");
|
|
171
|
+
if (!globalThis.__AKAN_EMBED__) window.parent.postMessage({ type: "pathChange", page }, "*");
|
|
172
172
|
window.scrollTo({ top: 0, behavior: "instant" });
|
|
173
173
|
}
|
|
174
174
|
},
|
package/ui/Modal.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
import { Dialog } from "./Dialog";
|
|
6
|
+
import type { ModalPlacement, ModalPresentation } from "./Dialog/modalSurface";
|
|
6
7
|
import { createOverridable } from "./UiOverride";
|
|
7
8
|
|
|
8
9
|
export interface ModalProps {
|
|
@@ -17,6 +18,10 @@ export interface ModalProps {
|
|
|
17
18
|
bodyClassName?: string;
|
|
18
19
|
children?: ReactNode;
|
|
19
20
|
confirmClose?: boolean;
|
|
21
|
+
/** `auto` is a full-screen sheet below `md`; the default stays a centred dialog. */
|
|
22
|
+
presentation?: ModalPresentation;
|
|
23
|
+
/** `right` is a full-height side sheet from `md` up. */
|
|
24
|
+
placement?: ModalPlacement;
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
export const DefaultModal = ({
|
|
@@ -30,6 +35,8 @@ export const DefaultModal = ({
|
|
|
30
35
|
bodyClassName,
|
|
31
36
|
children,
|
|
32
37
|
confirmClose = false,
|
|
38
|
+
presentation,
|
|
39
|
+
placement,
|
|
33
40
|
}: ModalProps) => {
|
|
34
41
|
return (
|
|
35
42
|
<Dialog open={open}>
|
|
@@ -40,6 +47,8 @@ export const DefaultModal = ({
|
|
|
40
47
|
bodyClassName={bodyClassName}
|
|
41
48
|
confirmClose={confirmClose}
|
|
42
49
|
closeButton={closeButton}
|
|
50
|
+
presentation={presentation}
|
|
51
|
+
placement={placement}
|
|
43
52
|
>
|
|
44
53
|
{title ? <Dialog.Title>{title}</Dialog.Title> : null}
|
|
45
54
|
<Dialog.Content>{children}</Dialog.Content>
|
package/ui/Model/EditModal.tsx
CHANGED
|
@@ -10,9 +10,11 @@ import { AiOutlinePlus, AiOutlineSave } from "react-icons/ai";
|
|
|
10
10
|
|
|
11
11
|
import { agentAttrs } from "../agentAttrs";
|
|
12
12
|
import { Button } from "../Button";
|
|
13
|
+
import type { ModalPlacement, ModalPresentation } from "../Dialog/modalSurface";
|
|
13
14
|
import { Modal } from "../Modal";
|
|
14
15
|
import DraftBar from "./DraftBar";
|
|
15
16
|
import { type DraftProp, editDraftScope, newDraftScope } from "./draftScope";
|
|
17
|
+
import { NewFormOrigin } from "./newFormOrigin";
|
|
16
18
|
|
|
17
19
|
const EDIT_PAYLOAD_MAX_AGE_MS = 60_000;
|
|
18
20
|
|
|
@@ -113,6 +115,10 @@ interface EditModalProps<Full extends { id: string }> extends EditModelProps<Ful
|
|
|
113
115
|
disabled?: boolean;
|
|
114
116
|
checkSubmit?: boolean;
|
|
115
117
|
modalClassName?: string;
|
|
118
|
+
/** Passed to the `Modal`: `auto` is a full-screen sheet below `md`. */
|
|
119
|
+
presentation?: ModalPresentation;
|
|
120
|
+
/** Passed to the `Modal`: `right` is a side sheet from `md` up. */
|
|
121
|
+
placement?: ModalPlacement;
|
|
116
122
|
renderTitle?: ((model: Full) => string | ReactNode) | string;
|
|
117
123
|
submitText?: ReactNode;
|
|
118
124
|
submitClassName?: string;
|
|
@@ -134,6 +140,8 @@ export default function EditModal<Full extends { id: string }>({
|
|
|
134
140
|
disabled,
|
|
135
141
|
checkSubmit = true,
|
|
136
142
|
modalClassName,
|
|
143
|
+
presentation,
|
|
144
|
+
placement,
|
|
137
145
|
edit,
|
|
138
146
|
modal,
|
|
139
147
|
renderTitle,
|
|
@@ -182,10 +190,15 @@ export default function EditModal<Full extends { id: string }>({
|
|
|
182
190
|
);
|
|
183
191
|
const modelFormLoading = storeUse[names.modelFormLoading]() as string | boolean;
|
|
184
192
|
const modalId = id ?? ((modelEdit as DynamicRecord)?.[names.modelObj] as Full | undefined)?.id ?? undefined;
|
|
193
|
+
const newFormOrigin = NewFormOrigin.use(modelName);
|
|
185
194
|
const isModalOpen =
|
|
186
195
|
modelModal === (modal ?? "edit") &&
|
|
187
196
|
(modelFormLoading === false || modelFormLoading === modalId) &&
|
|
188
|
-
((!modelFormId && !modalId
|
|
197
|
+
((!modelFormId && !modalId && (!newFormOrigin || newFormOrigin === sliceName)) ||
|
|
198
|
+
(!!modalId && modalId === modelFormId));
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (!modelModal) NewFormOrigin.clear(modelName);
|
|
201
|
+
}, [modelModal]);
|
|
189
202
|
const isEditPayloadStale = useCallback((viewAt?: Date | null) => {
|
|
190
203
|
if (isRscNavigationFromCache()) return true;
|
|
191
204
|
return (
|
|
@@ -346,6 +359,8 @@ export default function EditModal<Full extends { id: string }>({
|
|
|
346
359
|
handleCancel();
|
|
347
360
|
}}
|
|
348
361
|
className={modalClassName}
|
|
362
|
+
presentation={presentation}
|
|
363
|
+
placement={placement}
|
|
349
364
|
title={<Title />}
|
|
350
365
|
action={<Submit />}
|
|
351
366
|
>
|
package/ui/Model/New.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import { usePage } from "akanjs/client";
|
|
|
2
2
|
import type { SliceMeta } from "akanjs/fetch";
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
import { AiOutlinePlus } from "react-icons/ai";
|
|
5
|
+
import type { ModalPlacement, ModalPresentation } from "../Dialog/modalSurface";
|
|
5
6
|
import { buttonRecipe } from "../recipe";
|
|
6
7
|
import type { DraftProp } from "./draftScope";
|
|
7
8
|
import EditModal from "./EditModal";
|
|
@@ -18,9 +19,24 @@ interface NewProps<Full = any> {
|
|
|
18
19
|
namespace?: string;
|
|
19
20
|
/** Draft recovery for the form. `false` turns it off; a string names the scope explicitly. */
|
|
20
21
|
draft?: DraftProp;
|
|
22
|
+
/** Passed to the form's `Modal`: `auto` is a full-screen sheet below `md`. */
|
|
23
|
+
presentation?: ModalPresentation;
|
|
24
|
+
/** Passed to the form's `Modal`: `right` is a side sheet from `md` up. */
|
|
25
|
+
placement?: ModalPlacement;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
export default function New({
|
|
28
|
+
export default function New({
|
|
29
|
+
trigger,
|
|
30
|
+
children,
|
|
31
|
+
slice,
|
|
32
|
+
modal,
|
|
33
|
+
partial,
|
|
34
|
+
renderTitle,
|
|
35
|
+
namespace,
|
|
36
|
+
draft,
|
|
37
|
+
presentation,
|
|
38
|
+
placement,
|
|
39
|
+
}: NewProps) {
|
|
24
40
|
const { l } = usePage();
|
|
25
41
|
return (
|
|
26
42
|
<>
|
|
@@ -38,7 +54,14 @@ export default function New({ trigger, children, slice, modal, partial, renderTi
|
|
|
38
54
|
</button>
|
|
39
55
|
)}
|
|
40
56
|
</NewWrapper>
|
|
41
|
-
<EditModal
|
|
57
|
+
<EditModal
|
|
58
|
+
renderTitle={renderTitle}
|
|
59
|
+
slice={slice}
|
|
60
|
+
modal={modal ?? undefined}
|
|
61
|
+
draft={draft}
|
|
62
|
+
presentation={presentation}
|
|
63
|
+
placement={placement}
|
|
64
|
+
>
|
|
42
65
|
{children}
|
|
43
66
|
</EditModal>
|
|
44
67
|
</>
|