akanjs 3.0.0-alpha.2 → 3.0.0-alpha.21
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/README.ko.md +1 -1
- package/README.md +1 -1
- package/base/symbols.ts +4 -0
- package/client/clientRuntime.ts +8 -0
- package/client/cn.ts +7 -0
- package/client/csrTypes.ts +2 -0
- package/client/frameDebug.ts +3 -2
- package/common/Logger.ts +4 -0
- package/common/deepObjectify.ts +21 -7
- package/common/index.ts +14 -0
- package/common/mcpExposure.ts +99 -0
- package/common/pathSet.ts +17 -5
- package/common/routeConvention.ts +30 -0
- package/constant/crystalize.ts +3 -1
- package/constant/fieldInfo.ts +6 -0
- package/constant/getDefault.ts +37 -9
- package/constant/immerify.ts +1 -1
- package/constant/index.ts +2 -0
- package/constant/labelOf.ts +17 -0
- package/constant/mask.ts +60 -0
- package/constant/purify.ts +17 -11
- package/constant/types.ts +14 -12
- package/dictionary/DictionaryLookup.ts +30 -0
- package/dictionary/agent.dictionary.ts +31 -0
- package/dictionary/agentTurn.dictionary.ts +29 -0
- package/dictionary/base.dictionary.ts +8 -0
- package/dictionary/dictInfo.ts +8 -0
- package/dictionary/dictionary.ts +9 -3
- package/dictionary/index.ts +1 -0
- package/document/database.ts +21 -0
- package/document/filterMeta.ts +7 -0
- package/document/index.ts +1 -0
- package/document/into.ts +7 -1
- package/document/noDocumentError.ts +12 -0
- package/fetch/agentTurn.ts +19 -0
- package/fetch/client/fetchClient.ts +71 -13
- package/fetch/client/wsClient.ts +17 -2
- package/fetch/fetchType/endpointFetch.type.ts +10 -4
- package/fetch/index.ts +1 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db-wal +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-wal +0 -0
- package/package.json +11 -9
- package/server/akanApp.ts +30 -34
- package/server/akanOption.ts +65 -7
- package/server/akanServer.ts +172 -7
- package/server/animatedImage.ts +83 -0
- package/server/artifact/routeClientCache.ts +14 -0
- package/server/assetEncoding.ts +47 -0
- package/server/devtools/signalSerializer.ts +2 -2
- package/server/devtools/types.ts +2 -2
- package/server/di/diLifecycle.ts +31 -3
- package/server/di/predefinedAdaptor.ts +6 -0
- package/server/imageOptimizer.ts +33 -46
- package/server/index.ts +1 -2
- package/server/mcp/McpAuth.ts +177 -0
- package/server/mcp/McpDispatcher.ts +245 -0
- package/server/mcp/McpEventStream.ts +76 -0
- package/server/mcp/McpExecutionContext.ts +105 -0
- package/server/mcp/McpRouter.ts +591 -0
- package/server/mcp/index.ts +5 -0
- package/server/resolver/CascadeRunner.ts +1 -1
- package/server/resolver/database.resolver.ts +42 -5
- package/server/resolver/service.resolver.ts +29 -0
- package/server/resolver/signal.resolver.ts +16 -1
- package/server/routeTreeBuilder.ts +2 -44
- package/server/rscWorker.tsx +6 -2
- package/server/subRouteIndexDocument.tsx +61 -0
- package/server/systemPageDocument.tsx +2 -2
- package/server/systemPages.tsx +49 -16
- package/server/vendor/akanjs-fetch.ts +1 -0
- package/server/webRouter.ts +159 -35
- package/service/agent.service.ts +14 -0
- package/service/base.service.ts +2 -1
- package/service/index.ts +1 -0
- package/service/predefinedAdaptor/database.adaptor.ts +78 -24
- package/service/predefinedAdaptor/deepseekLlm.ts +230 -0
- package/service/predefinedAdaptor/index.ts +3 -0
- package/service/predefinedAdaptor/insightQuery.ts +183 -0
- package/service/predefinedAdaptor/llm.adaptor.ts +76 -0
- package/service/predefinedAdaptor/role.adaptor.ts +2 -0
- package/service/predefinedAdaptor/schedule.adaptor.ts +12 -5
- package/service/serviceModule.ts +17 -0
- package/service/types.ts +4 -0
- package/signal/agent/AgentCatalogue.ts +165 -0
- package/signal/agent/index.ts +1 -0
- package/signal/agent.signal.ts +42 -0
- package/signal/agentTurn.ts +8 -0
- package/signal/agentTurnStream.ts +48 -0
- package/signal/base.signal.ts +2 -1
- package/signal/endpointInfo.ts +38 -2
- package/signal/guard.ts +13 -1
- package/signal/guards.ts +52 -1
- package/signal/index.ts +6 -0
- package/signal/mcp/McpDocument.ts +286 -0
- package/signal/mcp/McpProgress.ts +106 -0
- package/signal/mcp/McpUriTemplate.ts +85 -0
- package/signal/mcp/Msg.ts +368 -0
- package/signal/mcp/index.ts +5 -0
- package/signal/mcp/mcpProtocol.ts +120 -0
- package/signal/middleware.ts +7 -3
- package/signal/openapi/openapi.ts +32 -171
- package/signal/schema/JsonSchemaBuilder.ts +211 -0
- package/signal/schema/index.ts +1 -0
- package/signal/signalContext.ts +85 -9
- package/signal/slice.ts +9 -9
- package/signal/types.ts +1 -1
- package/store/action.ts +11 -22
- package/store/actionTag.ts +33 -0
- package/store/agent/AgentBridge.ts +323 -0
- package/store/agent/AgentContext.ts +74 -0
- package/store/agent/AgentPrompts.ts +82 -0
- package/store/agent/AgentVisibility.ts +68 -0
- package/store/agent/ScreenReader.ts +208 -0
- package/store/agent/StoreCatalogue.ts +351 -0
- package/store/agent/StoreSurfaceSource.ts +122 -0
- package/store/agent/index.ts +9 -0
- package/store/agent/storeSurface.ts +28 -0
- package/store/agent/types.ts +50 -0
- package/store/agentic/StToolBuilder.ts +199 -0
- package/store/agentic/attachAgentic.ts +22 -0
- package/store/agentic/index.ts +5 -0
- package/store/agentic/readableValue.ts +17 -0
- package/store/agentic/useStExpose.ts +18 -0
- package/store/agentic/useStState.ts +35 -0
- package/store/baseSt.ts +7 -0
- package/store/databaseStateNames.ts +31 -0
- package/store/formSetterNames.ts +21 -0
- package/store/index.ts +8 -0
- package/store/rootStore.ts +3 -1
- package/store/sliceRole.ts +36 -0
- package/store/state.ts +2 -12
- package/store/store.ts +19 -1
- package/store/storeInstance.ts +165 -20
- package/store/storeRegistry.ts +16 -1
- package/store/types.ts +18 -0
- package/store/withSelector.ts +7 -8
- package/test/registerDom.ts +4 -0
- package/types/base/symbols.d.ts +4 -0
- package/types/client/cn.d.ts +5 -0
- package/types/client/csrTypes.d.ts +2 -0
- package/types/common/Logger.d.ts +2 -0
- package/types/common/deepObjectify.d.ts +4 -2
- package/types/common/index.d.ts +2 -1
- package/types/common/mcpExposure.d.ts +60 -0
- package/types/common/routeConvention.d.ts +8 -0
- package/types/constant/fieldInfo.d.ts +1 -0
- package/types/constant/getDefault.d.ts +5 -0
- package/types/constant/index.d.ts +2 -0
- package/types/constant/labelOf.d.ts +6 -0
- package/types/constant/mask.d.ts +34 -0
- package/types/constant/purify.d.ts +2 -2
- package/types/constant/types.d.ts +2 -2
- package/types/dictionary/DictionaryLookup.d.ts +14 -0
- package/types/dictionary/agent.dictionary.d.ts +1 -0
- package/types/dictionary/agentTurn.dictionary.d.ts +1 -0
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictInfo.d.ts +6 -0
- package/types/dictionary/dictionary.d.ts +11 -9
- package/types/dictionary/index.d.ts +1 -0
- package/types/document/database.d.ts +20 -0
- package/types/document/filterMeta.d.ts +1 -0
- package/types/document/index.d.ts +1 -0
- package/types/document/into.d.ts +6 -1
- package/types/document/noDocumentError.d.ts +12 -0
- package/types/fetch/agentTurn.d.ts +593 -0
- package/types/fetch/client/fetchClient.d.ts +10 -0
- package/types/fetch/fetchType/endpointFetch.type.d.ts +5 -3
- package/types/fetch/index.d.ts +1 -0
- package/types/server/akanOption.d.ts +35 -7
- package/types/server/akanServer.d.ts +44 -1
- package/types/server/animatedImage.d.ts +7 -0
- package/types/server/artifact/routeClientCache.d.ts +6 -0
- package/types/server/assetEncoding.d.ts +7 -0
- package/types/server/devtools/types.d.ts +2 -2
- package/types/server/di/predefinedAdaptor.d.ts +4 -1
- package/types/server/index.d.ts +0 -2
- package/types/server/mcp/McpAuth.d.ts +62 -0
- package/types/server/mcp/McpDispatcher.d.ts +54 -0
- package/types/server/mcp/McpEventStream.d.ts +18 -0
- package/types/server/mcp/McpExecutionContext.d.ts +44 -0
- package/types/server/mcp/McpRouter.d.ts +60 -0
- package/types/server/mcp/index.d.ts +5 -0
- package/types/server/mcp.d.ts +1 -0
- package/types/server/subRouteIndexDocument.d.ts +8 -0
- package/types/server/systemPageDocument.d.ts +1 -0
- package/types/server/systemPages.d.ts +5 -0
- package/types/server/vendor/akanjs-fetch.d.ts +1 -0
- package/types/service/agent.service.d.ts +12 -0
- package/types/service/base.service.d.ts +4 -0
- package/types/service/index.d.ts +1 -0
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +24 -2
- package/types/service/predefinedAdaptor/deepseekLlm.d.ts +64 -0
- package/types/service/predefinedAdaptor/index.d.ts +3 -0
- package/types/service/predefinedAdaptor/insightQuery.d.ts +50 -0
- package/types/service/predefinedAdaptor/llm.adaptor.d.ts +68 -0
- package/types/service/predefinedAdaptor/role.adaptor.d.ts +2 -0
- package/types/service/predefinedAdaptor/schedule.adaptor.d.ts +10 -6
- package/types/service/types.d.ts +4 -1
- package/types/signal/agent/AgentCatalogue.d.ts +100 -0
- package/types/signal/agent/index.d.ts +1 -0
- package/types/signal/agent.d.ts +1 -0
- package/types/signal/agent.signal.d.ts +29 -0
- package/types/signal/agentTurn.d.ts +9 -0
- package/types/signal/agentTurnStream.d.ts +16 -0
- package/types/signal/base.signal.d.ts +1 -1
- package/types/signal/endpointInfo.d.ts +21 -3
- package/types/signal/guard.d.ts +11 -0
- package/types/signal/guards.d.ts +22 -1
- package/types/signal/index.d.ts +6 -0
- package/types/signal/mcp/McpDocument.d.ts +65 -0
- package/types/signal/mcp/McpProgress.d.ts +40 -0
- package/types/signal/mcp/McpUriTemplate.d.ts +26 -0
- package/types/signal/mcp/Msg.d.ts +143 -0
- package/types/signal/mcp/index.d.ts +5 -0
- package/types/signal/mcp/mcpProtocol.d.ts +107 -0
- package/types/signal/mcp.d.ts +1 -0
- package/types/signal/openapi/openapi.d.ts +3 -3
- package/types/signal/schema/JsonSchemaBuilder.d.ts +47 -0
- package/types/signal/schema/index.d.ts +1 -0
- package/types/signal/schema.d.ts +1 -0
- package/types/signal/signalContext.d.ts +16 -1
- package/types/signal/slice.d.ts +8 -8
- package/types/signal/types.d.ts +1 -1
- package/types/store/actionTag.d.ts +21 -0
- package/types/store/agent/AgentBridge.d.ts +77 -0
- package/types/store/agent/AgentContext.d.ts +16 -0
- package/types/store/agent/AgentPrompts.d.ts +31 -0
- package/types/store/agent/AgentVisibility.d.ts +21 -0
- package/types/store/agent/ScreenReader.d.ts +11 -0
- package/types/store/agent/StoreCatalogue.d.ts +26 -0
- package/types/store/agent/StoreSurfaceSource.d.ts +18 -0
- package/types/store/agent/index.d.ts +9 -0
- package/types/store/agent/storeSurface.d.ts +14 -0
- package/types/store/agent/types.d.ts +49 -0
- package/types/store/agent.d.ts +1 -0
- package/types/store/agentic/StToolBuilder.d.ts +41 -0
- package/types/store/agentic/attachAgentic.d.ts +14 -0
- package/types/store/agentic/index.d.ts +5 -0
- package/types/store/agentic/readableValue.d.ts +3 -0
- package/types/store/agentic/useStExpose.d.ts +8 -0
- package/types/store/agentic/useStState.d.ts +12 -0
- package/types/store/agentic.d.ts +1 -0
- package/types/store/baseSt.d.ts +29 -22
- package/types/store/databaseStateNames.d.ts +25 -0
- package/types/store/formSetterNames.d.ts +16 -0
- package/types/store/index.d.ts +7 -0
- package/types/store/rootStore.d.ts +5 -1
- package/types/store/sliceRole.d.ts +25 -0
- package/types/store/store.d.ts +7 -2
- package/types/store/storeInstance.d.ts +33 -1
- package/types/store/storeRegistry.d.ts +5 -0
- package/types/store/types.d.ts +15 -0
- package/types/store/withSelector.d.ts +7 -8
- package/types/test/registerDom.d.ts +1 -0
- package/types/ui/Agent/Approval.d.ts +7 -0
- package/types/ui/Agent/Bubble.d.ts +13 -0
- package/types/ui/Agent/Chat.d.ts +24 -0
- package/types/ui/Agent/Context.d.ts +9 -0
- package/types/ui/Agent/Dock.d.ts +16 -0
- package/types/ui/Agent/Guide.d.ts +9 -0
- package/types/ui/Agent/Section.d.ts +10 -0
- package/types/ui/Agent/StateKey.d.ts +16 -0
- package/types/ui/Agent/Tool.d.ts +15 -0
- package/types/ui/Agent/Transcript.d.ts +13 -0
- package/types/ui/Agent/Zone.d.ts +23 -0
- package/types/ui/Agent/fetchRunner.d.ts +12 -0
- package/types/ui/Agent/index.d.ts +17 -0
- package/types/ui/Agent/index_.d.ts +1 -0
- package/types/ui/Agent/sessionHistory.d.ts +12 -0
- package/types/ui/Agent.d.ts +1 -0
- package/types/ui/Badge.d.ts +1 -1
- package/types/ui/Button.d.ts +1 -1
- package/types/ui/CsrImage.d.ts +1 -1
- package/types/ui/Dropdown.d.ts +2 -0
- package/types/ui/Image.d.ts +3 -3
- package/types/ui/Layout/BottomInset.d.ts +2 -1
- package/types/ui/Layout/index.d.ts +1 -1
- package/types/ui/Signal/style.d.ts +4 -1
- package/types/ui/UiOverride/context.d.ts +14 -2
- package/types/ui/agentAttrs.d.ts +14 -0
- package/types/ui/index.d.ts +4 -1
- package/types/ui/overlayLayer.d.ts +24 -0
- package/types/ui/recipe/badgeRecipe.d.ts +7 -3
- package/types/ui/recipe/buttonRecipe.d.ts +7 -3
- package/types/ui/recipe/inputRecipe.d.ts +5 -1
- package/types/vendor/use-agentic/AgentProvider.d.ts +18 -0
- package/types/vendor/use-agentic/AgentScope.d.ts +9 -0
- package/types/vendor/use-agentic/AgentSession.d.ts +50 -0
- package/types/vendor/use-agentic/Agentic.d.ts +21 -0
- package/types/vendor/use-agentic/AgenticSurface.d.ts +44 -0
- package/types/vendor/use-agentic/httpRunner.d.ts +15 -0
- package/types/vendor/use-agentic/index.d.ts +13 -0
- package/types/vendor/use-agentic/surfaceContext.d.ts +5 -0
- package/types/vendor/use-agentic/types.d.ts +133 -0
- package/types/vendor/use-agentic/useAgent.d.ts +4 -0
- package/types/vendor/use-agentic/useAgentGuide.d.ts +6 -0
- package/types/vendor/use-agentic/useAgentResource.d.ts +7 -0
- package/types/vendor/use-agentic/useAgentState.d.ts +13 -0
- package/types/vendor/use-agentic/useAgentTool.d.ts +16 -0
- package/types/vendor/use-agentic.d.ts +1 -0
- package/types/webkit/index.d.ts +3 -0
- package/types/webkit/lazy.d.ts +12 -0
- package/types/webkit/useCsrValues.d.ts +3 -3
- package/types/webkit/useEscapeKey.d.ts +5 -0
- package/types/webkit/useFrameRuntime.d.ts +6 -1
- package/types/webkit/useScreenScope.d.ts +18 -0
- package/ui/Agent/Approval.tsx +29 -0
- package/ui/Agent/Bubble.tsx +74 -0
- package/ui/Agent/Chat.tsx +225 -0
- package/ui/Agent/Context.tsx +38 -0
- package/ui/Agent/Dock.tsx +72 -0
- package/ui/Agent/Guide.tsx +16 -0
- package/ui/Agent/Section.tsx +24 -0
- package/ui/Agent/StateKey.tsx +44 -0
- package/ui/Agent/Tool.tsx +66 -0
- package/ui/Agent/Transcript.tsx +33 -0
- package/ui/Agent/Zone.tsx +48 -0
- package/ui/Agent/fetchRunner.ts +30 -0
- package/ui/Agent/index.ts +12 -0
- package/ui/Agent/index_.tsx +4 -0
- package/ui/Agent/sessionHistory.ts +33 -0
- package/ui/Badge.tsx +3 -3
- package/ui/BottomSheet.tsx +5 -0
- package/ui/Button.tsx +6 -2
- package/ui/Constant/Doc.tsx +1 -1
- package/ui/CsrImage.tsx +1 -1
- package/ui/Data/ListContainer.tsx +1 -1
- package/ui/DatePicker.tsx +5 -4
- package/ui/Dialog/Modal.tsx +12 -15
- package/ui/DraggableList.tsx +3 -1
- package/ui/Dropdown.tsx +33 -11
- package/ui/Field.tsx +30 -16
- package/ui/Image.tsx +3 -3
- package/ui/Input.tsx +14 -5
- package/ui/Layout/BottomInset.tsx +6 -1
- package/ui/Load/Units.tsx +11 -2
- package/ui/Load/View.tsx +7 -2
- package/ui/Loading/ProgressBar.tsx +8 -1
- package/ui/Menu.tsx +7 -8
- package/ui/Model/EditModal.tsx +37 -2
- package/ui/Model/SureToRemove.tsx +2 -1
- package/ui/Model/index_.tsx +42 -15
- package/ui/ObjectId.tsx +3 -4
- package/ui/Pagination.tsx +3 -4
- package/ui/Popconfirm.tsx +8 -7
- package/ui/Select.tsx +6 -4
- package/ui/Signal/RestApi.tsx +43 -16
- package/ui/Signal/WebSocket.tsx +1 -1
- package/ui/Signal/style.ts +6 -1
- package/ui/Switch.tsx +2 -0
- package/ui/System/CSR.tsx +6 -2
- package/ui/System/ThemeToggle.tsx +55 -33
- package/ui/ToggleSelect.tsx +4 -3
- package/ui/Tooltip.tsx +2 -1
- package/ui/UiOverride/context.ts +14 -2
- package/ui/agentAttrs.ts +19 -0
- package/ui/index.ts +10 -1
- package/ui/overlayLayer.ts +39 -0
- package/ui/recipe/badgeRecipe.ts +22 -3
- package/ui/recipe/buttonRecipe.ts +51 -2
- package/ui/recipe/factory.ts +2 -2
- package/ui/recipe/inputRecipe.ts +18 -4
- package/vendor/use-agentic/AgentProvider.tsx +39 -0
- package/vendor/use-agentic/AgentScope.tsx +20 -0
- package/vendor/use-agentic/AgentSession.ts +292 -0
- package/vendor/use-agentic/Agentic.tsx +44 -0
- package/vendor/use-agentic/AgenticSurface.ts +294 -0
- package/vendor/use-agentic/WIRE.md +57 -0
- package/vendor/use-agentic/httpRunner.ts +115 -0
- package/vendor/use-agentic/index.ts +14 -0
- package/vendor/use-agentic/surfaceContext.ts +9 -0
- package/vendor/use-agentic/types.ts +144 -0
- package/vendor/use-agentic/useAgent.ts +17 -0
- package/vendor/use-agentic/useAgentGuide.ts +16 -0
- package/vendor/use-agentic/useAgentResource.ts +30 -0
- package/vendor/use-agentic/useAgentState.ts +61 -0
- package/vendor/use-agentic/useAgentTool.ts +55 -0
- package/webkit/bootCsr.tsx +2 -33
- package/webkit/index.ts +3 -0
- package/webkit/lazy.tsx +27 -3
- package/webkit/useCsrValues.ts +121 -4
- package/webkit/useEscapeKey.tsx +42 -0
- package/webkit/useFrameRuntime.ts +65 -32
- package/webkit/useScreenScope.tsx +49 -0
package/ui/DraggableList.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import { createContext, type ReactElement, type ReactNode, useContext, useRef }
|
|
|
7
7
|
import { BiTrash } from "react-icons/bi";
|
|
8
8
|
import { MdDragIndicator } from "react-icons/md";
|
|
9
9
|
import { buttonRecipe } from "./Button";
|
|
10
|
+
import { useUiRecipe } from "./UiOverride";
|
|
10
11
|
|
|
11
12
|
const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);
|
|
12
13
|
|
|
@@ -169,6 +170,7 @@ const Item = ({
|
|
|
169
170
|
removeClassName,
|
|
170
171
|
}: ItemProps) => {
|
|
171
172
|
const { onRemove } = useDragList();
|
|
173
|
+
const recipe = useUiRecipe("button") ?? buttonRecipe;
|
|
172
174
|
return (
|
|
173
175
|
<div className={cn("flex w-full items-center gap-2", className)}>
|
|
174
176
|
{cursor ? (
|
|
@@ -179,7 +181,7 @@ const Item = ({
|
|
|
179
181
|
{children}
|
|
180
182
|
{removable && (
|
|
181
183
|
<button
|
|
182
|
-
className={
|
|
184
|
+
className={recipe({ variant: "outline", size: "xs" }, [
|
|
183
185
|
"size-6 border-destructive p-0 text-destructive hover:bg-destructive hover:text-destructive-foreground",
|
|
184
186
|
removeClassName,
|
|
185
187
|
])}
|
package/ui/Dropdown.tsx
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "akanjs/client";
|
|
3
|
-
import { type ReactNode, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { type ReactNode, useEffect, useId, useRef, useState } from "react";
|
|
4
4
|
|
|
5
5
|
import { buttonRecipe } from "./Button";
|
|
6
|
-
import {
|
|
6
|
+
import { isOwnOverlayClick, OverlayOwnerProvider, useOverlayScope } from "./overlayLayer";
|
|
7
|
+
import { createOverridable, useUiRecipe } from "./UiOverride";
|
|
8
|
+
|
|
9
|
+
/** Put this on a menu item that runs its own interaction (a switch, a copy button) to keep the menu open. */
|
|
10
|
+
export const DROPDOWN_KEEP_OPEN_ATTR = "data-dropdown-keep-open";
|
|
11
|
+
|
|
12
|
+
const keepOpenSelector = `[${DROPDOWN_KEEP_OPEN_ATTR}]`;
|
|
7
13
|
|
|
8
14
|
export interface DropdownProps {
|
|
9
15
|
/** Button/trigger content. */
|
|
@@ -20,39 +26,55 @@ export interface DropdownProps {
|
|
|
20
26
|
|
|
21
27
|
export const DefaultDropdown = ({ value, content, className, buttonClassName, dropdownClassName }: DropdownProps) => {
|
|
22
28
|
const [opened, setOpened] = useState(false);
|
|
29
|
+
const [mounted, setMounted] = useState(false);
|
|
23
30
|
const ref = useRef<HTMLDivElement>(null);
|
|
31
|
+
const scope = useOverlayScope(useId());
|
|
32
|
+
|
|
33
|
+
const recipe = useUiRecipe("button") ?? buttonRecipe;
|
|
24
34
|
useEffect(() => {
|
|
25
35
|
if (!opened) return;
|
|
26
|
-
const
|
|
36
|
+
const onMouseDown = (e: MouseEvent) => {
|
|
37
|
+
|
|
38
|
+
if (isOwnOverlayClick(e.target, scope)) return;
|
|
27
39
|
if (ref.current && !ref.current.contains(e.target as Node)) setOpened(false);
|
|
28
40
|
};
|
|
29
|
-
document.addEventListener("mousedown",
|
|
41
|
+
document.addEventListener("mousedown", onMouseDown);
|
|
30
42
|
return () => {
|
|
31
|
-
document.removeEventListener("mousedown",
|
|
43
|
+
document.removeEventListener("mousedown", onMouseDown);
|
|
32
44
|
};
|
|
33
|
-
}, [opened]);
|
|
45
|
+
}, [opened, scope]);
|
|
34
46
|
return (
|
|
35
47
|
<div ref={ref} className={cn("relative inline-block", className)}>
|
|
36
48
|
<button
|
|
37
49
|
type="button"
|
|
38
|
-
|
|
50
|
+
aria-haspopup="menu"
|
|
51
|
+
aria-expanded={opened}
|
|
52
|
+
className={recipe({ variant: "ghost" }, ["flex", buttonClassName])}
|
|
39
53
|
onClick={() => {
|
|
54
|
+
setMounted(true);
|
|
40
55
|
setOpened((o) => !o);
|
|
41
56
|
}}
|
|
42
57
|
>
|
|
43
58
|
{value}
|
|
44
59
|
</button>
|
|
45
|
-
{opened
|
|
60
|
+
{/* Hidden rather than unmounted once opened: unmounting takes any overlay a menu item opened down with it. */}
|
|
61
|
+
{mounted ? (
|
|
46
62
|
<ul
|
|
47
|
-
|
|
63
|
+
hidden={!opened}
|
|
64
|
+
onClick={(e) => {
|
|
65
|
+
|
|
66
|
+
if (isOwnOverlayClick(e.target, scope)) return;
|
|
67
|
+
if (e.target instanceof Element && e.target.closest(keepOpenSelector)) return;
|
|
48
68
|
setOpened(false);
|
|
49
69
|
}}
|
|
50
70
|
className={cn(
|
|
51
|
-
"absolute right-0 z-[100] mt-1 grid max-h-52 gap-
|
|
71
|
+
"absolute right-0 z-[100] mt-1 grid max-h-52 gap-0.5 overflow-auto whitespace-nowrap rounded-md bg-popover p-1 text-popover-foreground shadow-md",
|
|
52
72
|
dropdownClassName,
|
|
73
|
+
!opened && "hidden",
|
|
53
74
|
)}
|
|
54
75
|
>
|
|
55
|
-
{
|
|
76
|
+
{/* Reaches an overlay this menu opens even after it portals away, so it can claim it as its own. */}
|
|
77
|
+
<OverlayOwnerProvider value={scope}>{content}</OverlayOwnerProvider>
|
|
56
78
|
</ul>
|
|
57
79
|
) : null}
|
|
58
80
|
</div>
|
package/ui/Field.tsx
CHANGED
|
@@ -9,14 +9,17 @@ import { AiOutlinePlus } from "react-icons/ai";
|
|
|
9
9
|
import { BiHelpCircle, BiTrash, BiX } from "react-icons/bi";
|
|
10
10
|
import { MdDragIndicator } from "react-icons/md";
|
|
11
11
|
|
|
12
|
+
import { agentAttrs } from "./agentAttrs";
|
|
12
13
|
import { badgeRecipe } from "./Badge";
|
|
13
14
|
import { buttonRecipe } from "./Button";
|
|
14
15
|
import { DraggableList } from "./DraggableList";
|
|
15
16
|
import { Input } from "./Input";
|
|
17
|
+
import { inputRecipe } from "./recipe";
|
|
16
18
|
import { Select } from "./Select";
|
|
17
19
|
import { Switch as UiSwitch } from "./Switch";
|
|
18
20
|
import { ToggleSelect as UtilToggleSelect } from "./ToggleSelect";
|
|
19
21
|
import { Tooltip } from "./Tooltip";
|
|
22
|
+
import { useUiRecipe } from "./UiOverride";
|
|
20
23
|
|
|
21
24
|
interface LabelProps {
|
|
22
25
|
className?: string;
|
|
@@ -101,6 +104,7 @@ const List = <Item,>({
|
|
|
101
104
|
renderItem,
|
|
102
105
|
}: ListProps<Item>) => {
|
|
103
106
|
const { l } = usePage();
|
|
107
|
+
const recipe = useUiRecipe("button") ?? buttonRecipe;
|
|
104
108
|
return (
|
|
105
109
|
<div className={cn("flex w-full flex-col", className)}>
|
|
106
110
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
@@ -111,7 +115,7 @@ const List = <Item,>({
|
|
|
111
115
|
{renderItem(item, idx)}
|
|
112
116
|
<div className="flex gap-2 border-border border-l pl-2">
|
|
113
117
|
<button
|
|
114
|
-
className={
|
|
118
|
+
className={recipe(
|
|
115
119
|
{ variant: "outline", size: "icon" },
|
|
116
120
|
"size-6 border-destructive p-0 text-destructive hover:bg-destructive hover:text-destructive-foreground",
|
|
117
121
|
)}
|
|
@@ -127,7 +131,7 @@ const List = <Item,>({
|
|
|
127
131
|
</>
|
|
128
132
|
))}
|
|
129
133
|
<button
|
|
130
|
-
className={
|
|
134
|
+
className={recipe({ variant: "outline" })}
|
|
131
135
|
onClick={() => {
|
|
132
136
|
onAdd();
|
|
133
137
|
}}
|
|
@@ -183,6 +187,7 @@ const Text = ({
|
|
|
183
187
|
<div className={cn("flex flex-col", className)}>
|
|
184
188
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
185
189
|
<Input
|
|
190
|
+
{...agentAttrs(onChange)}
|
|
186
191
|
cacheKey={cache ? `${label}-${desc}-text` : undefined}
|
|
187
192
|
inputStyleType={inputStyleType}
|
|
188
193
|
value={value ?? ""}
|
|
@@ -247,6 +252,7 @@ const Price = ({
|
|
|
247
252
|
<div className={cn("flex flex-col", className)}>
|
|
248
253
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
249
254
|
<Input
|
|
255
|
+
{...agentAttrs(onChange)}
|
|
250
256
|
inputStyleType={inputStyleType}
|
|
251
257
|
value={value ?? ""}
|
|
252
258
|
nullable={nullable}
|
|
@@ -313,6 +319,7 @@ const TextArea = ({
|
|
|
313
319
|
<div className={cn("flex flex-col", className)}>
|
|
314
320
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
315
321
|
<Input.TextArea
|
|
322
|
+
{...agentAttrs(onChange)}
|
|
316
323
|
value={value ?? ""}
|
|
317
324
|
cacheKey={cache ? `${label}-${desc}-textArea` : undefined}
|
|
318
325
|
nullable={nullable}
|
|
@@ -361,7 +368,7 @@ const Switch = ({
|
|
|
361
368
|
offDesc,
|
|
362
369
|
}: SwitchProps) => {
|
|
363
370
|
return (
|
|
364
|
-
<div className={cn("flex flex-col", className)}>
|
|
371
|
+
<div {...agentAttrs(onChange)} className={cn("flex flex-col", className)}>
|
|
365
372
|
{label ? <Label className={labelClassName} nullable label={label} desc={desc} /> : null}
|
|
366
373
|
<div className="flex items-center gap-2">
|
|
367
374
|
<UiSwitch
|
|
@@ -411,7 +418,7 @@ const ToggleSelect = <I extends string | number | boolean | null>({
|
|
|
411
418
|
const { l } = usePage();
|
|
412
419
|
const isEnumValue = isEnum(items as EnumInstance<string, I>);
|
|
413
420
|
return (
|
|
414
|
-
<div className={cn("flex flex-col", className)}>
|
|
421
|
+
<div {...agentAttrs(onChange)} className={cn("flex flex-col", className)}>
|
|
415
422
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
416
423
|
<UtilToggleSelect
|
|
417
424
|
className="mt-2"
|
|
@@ -468,7 +475,7 @@ const MultiToggleSelect = <I extends string | number | boolean>({
|
|
|
468
475
|
const { l } = usePage();
|
|
469
476
|
const isEnumValue = isEnum(items as EnumInstance<string, I>);
|
|
470
477
|
return (
|
|
471
|
-
<div className={cn("flex flex-col", className)}>
|
|
478
|
+
<div {...agentAttrs(onChange)} className={cn("flex flex-col", className)}>
|
|
472
479
|
{label ? <Label className={labelClassName} nullable={!!minlength} label={label} desc={desc} /> : null}
|
|
473
480
|
<UtilToggleSelect.Multi
|
|
474
481
|
nullable={!minlength}
|
|
@@ -533,8 +540,9 @@ const TextList = ({
|
|
|
533
540
|
inputClassName,
|
|
534
541
|
}: TextListProps) => {
|
|
535
542
|
const { l } = usePage();
|
|
543
|
+
const recipe = useUiRecipe("button") ?? buttonRecipe;
|
|
536
544
|
return (
|
|
537
|
-
<div className={cn("flex flex-col", className)}>
|
|
545
|
+
<div {...agentAttrs(onChange)} className={cn("flex flex-col", className)}>
|
|
538
546
|
{label ? <Label className={labelClassName} nullable={!minlength} label={label} desc={desc} /> : null}
|
|
539
547
|
<div className="mb-5 h-full gap-2 rounded-md border border-border p-2">
|
|
540
548
|
<DraggableList
|
|
@@ -570,7 +578,7 @@ const TextList = ({
|
|
|
570
578
|
disabled={disabled}
|
|
571
579
|
/>
|
|
572
580
|
<button
|
|
573
|
-
className={
|
|
581
|
+
className={recipe(
|
|
574
582
|
{ variant: "outline", size: "icon" },
|
|
575
583
|
"size-6 border-destructive p-0 text-destructive hover:bg-destructive hover:text-destructive-foreground",
|
|
576
584
|
)}
|
|
@@ -588,7 +596,7 @@ const TextList = ({
|
|
|
588
596
|
<div className="my-5 h-[0.5px] bg-foreground/20" />
|
|
589
597
|
{value.length <= maxTextlength ? (
|
|
590
598
|
<button
|
|
591
|
-
className={
|
|
599
|
+
className={recipe({ variant: "outline" }, "w-full")}
|
|
592
600
|
onClick={() => {
|
|
593
601
|
onChange([...value, ""]);
|
|
594
602
|
}}
|
|
@@ -638,6 +646,7 @@ const Tags = ({
|
|
|
638
646
|
inputClassName,
|
|
639
647
|
}: TagsProps) => {
|
|
640
648
|
const { l } = usePage();
|
|
649
|
+
const badge = useUiRecipe("badge") ?? badgeRecipe;
|
|
641
650
|
const [inputVisible, setInputVisible] = useState(false);
|
|
642
651
|
const [tag, setTag] = useState("");
|
|
643
652
|
const addTag = () => {
|
|
@@ -648,11 +657,11 @@ const Tags = ({
|
|
|
648
657
|
};
|
|
649
658
|
|
|
650
659
|
return (
|
|
651
|
-
<div className={cn("flex flex-col", className)}>
|
|
660
|
+
<div {...agentAttrs(onChange)} className={cn("flex flex-col", className)}>
|
|
652
661
|
{label ? <Label className={labelClassName} nullable={!minlength} label={label} desc={desc} /> : null}
|
|
653
662
|
<div className="flex w-full flex-wrap items-center gap-1 rounded-md border border-foreground/20 p-2">
|
|
654
663
|
{value.map((val, idx) => (
|
|
655
|
-
<span className={
|
|
664
|
+
<span className={badge({ variant: "outline" }, "items-center")} key={idx}>
|
|
656
665
|
<div className="text-xs italic">#</div>
|
|
657
666
|
{val}
|
|
658
667
|
<BiX
|
|
@@ -731,15 +740,16 @@ const Date = <Nullable extends boolean>({
|
|
|
731
740
|
dateClassName,
|
|
732
741
|
}: DateProps<Nullable>) => {
|
|
733
742
|
return (
|
|
734
|
-
<div className={cn("flex flex-col", className)}>
|
|
743
|
+
<div {...agentAttrs(onChange)} className={cn("flex flex-col", className)}>
|
|
735
744
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
736
|
-
{/*
|
|
745
|
+
{/* FIXME: daysi UI datetime-local 컴포넌트에 max 값 넣으면 오른쪽 끝 짤리는 버그 있음.*/}
|
|
737
746
|
<input
|
|
738
747
|
type={showTime ? "datetime-local" : "date"}
|
|
739
|
-
className={
|
|
740
|
-
|
|
748
|
+
className={inputRecipe({}, [
|
|
749
|
+
|
|
750
|
+
"user-invalid:border-destructive text-xs outline-none duration-200 focus-within:outline-none focus:outline-none",
|
|
741
751
|
dateClassName,
|
|
742
|
-
)}
|
|
752
|
+
])}
|
|
743
753
|
min={min ? (showTime ? dayjs(min).format("YYYY-MM-DDTHH:mm") : dayjs(min).format("YYYY-MM-DD")) : undefined}
|
|
744
754
|
max={max ? (showTime ? dayjs(max).format("YYYY-MM-DDTHH:mm") : dayjs(max).format("YYYY-MM-DD")) : undefined}
|
|
745
755
|
value={value ? (showTime ? dayjs(value).format("YYYY-MM-DDTHH:mm") : dayjs(value).format("YYYY-MM-DD")) : ""}
|
|
@@ -868,6 +878,7 @@ const Number = ({
|
|
|
868
878
|
<div className={cn("flex flex-col", className)}>
|
|
869
879
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} unit={unit} /> : null}
|
|
870
880
|
<Input.Number
|
|
881
|
+
{...agentAttrs(onChange)}
|
|
871
882
|
min={min}
|
|
872
883
|
max={max}
|
|
873
884
|
cacheKey={cache ? `${label}-${desc}-number` : undefined}
|
|
@@ -935,7 +946,7 @@ const DoubleNumber = ({
|
|
|
935
946
|
}: DoubleNumberProps) => {
|
|
936
947
|
const { l } = usePage();
|
|
937
948
|
return (
|
|
938
|
-
<div className={cn("flex flex-col", className)}>
|
|
949
|
+
<div {...agentAttrs(onChange)} className={cn("flex flex-col", className)}>
|
|
939
950
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
940
951
|
<div className="flex items-center gap-2">
|
|
941
952
|
<Input.Number
|
|
@@ -1025,6 +1036,7 @@ const Email = ({
|
|
|
1025
1036
|
<div className={cn("flex flex-col", className)}>
|
|
1026
1037
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
1027
1038
|
<Input.Email
|
|
1039
|
+
{...agentAttrs(onChange)}
|
|
1028
1040
|
value={value ?? ""}
|
|
1029
1041
|
cacheKey={cache ? `${label}-${desc}-email` : undefined}
|
|
1030
1042
|
nullable={nullable}
|
|
@@ -1089,6 +1101,7 @@ const Phone = ({
|
|
|
1089
1101
|
<div className={cn("flex flex-col", className)}>
|
|
1090
1102
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
1091
1103
|
<Input
|
|
1104
|
+
{...agentAttrs(onChange)}
|
|
1092
1105
|
value={value ?? ""}
|
|
1093
1106
|
cacheKey={cache ? `${label}-${desc}-phone` : undefined}
|
|
1094
1107
|
nullable={nullable}
|
|
@@ -1159,6 +1172,7 @@ const Password = ({
|
|
|
1159
1172
|
{label ? <Label className={labelClassName} nullable={nullable} label={label} desc={desc} /> : null}
|
|
1160
1173
|
<div className="flex flex-col gap-2">
|
|
1161
1174
|
<Input.Password
|
|
1175
|
+
{...agentAttrs(onChange)}
|
|
1162
1176
|
cacheKey={cache ? `${label}-${desc}-password` : undefined}
|
|
1163
1177
|
value={value ?? ""}
|
|
1164
1178
|
nullable={nullable}
|
package/ui/Image.tsx
CHANGED
|
@@ -30,7 +30,7 @@ type AkanImageProps = NativeImageProps & {
|
|
|
30
30
|
/** Akan file object or file-like value with url and imageSize metadata. */
|
|
31
31
|
file?: ImageLikeFile;
|
|
32
32
|
/** Low-quality preview data. Overrides file.abstractData when provided. */
|
|
33
|
-
abstractData?: string;
|
|
33
|
+
abstractData?: string | null;
|
|
34
34
|
/** Accessible alt text. Defaults to "image" when omitted. */
|
|
35
35
|
alt?: string;
|
|
36
36
|
/** Image optimizer quality. Defaults to 75. */
|
|
@@ -59,12 +59,12 @@ export const Image = ({
|
|
|
59
59
|
| {
|
|
60
60
|
src?: string;
|
|
61
61
|
file?: ProtoFile;
|
|
62
|
-
abstractData?: string;
|
|
62
|
+
abstractData?: string | null;
|
|
63
63
|
alt?: string;
|
|
64
64
|
}
|
|
65
65
|
| {
|
|
66
66
|
src?: undefined;
|
|
67
|
-
abstractData?: string;
|
|
67
|
+
abstractData?: string | null;
|
|
68
68
|
file: { url: string; imageSize: [number, number]; abstractData?: string | null } | null;
|
|
69
69
|
alt?: string;
|
|
70
70
|
}
|
package/ui/Input.tsx
CHANGED
|
@@ -13,11 +13,9 @@ import React, {
|
|
|
13
13
|
} from "react";
|
|
14
14
|
import { AiOutlineEye, AiOutlineEyeInvisible } from "react-icons/ai";
|
|
15
15
|
|
|
16
|
+
import { agentAttrs } from "./agentAttrs";
|
|
16
17
|
import { inputRecipe } from "./recipe";
|
|
17
|
-
import { createOverridable } from "./UiOverride";
|
|
18
|
-
|
|
19
|
-
const inputBase = inputRecipe();
|
|
20
|
-
const textareaBase = inputRecipe({ kind: "area" });
|
|
18
|
+
import { createOverridable, useUiRecipe } from "./UiOverride";
|
|
21
19
|
|
|
22
20
|
export type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange"> & {
|
|
23
21
|
/** Visual input style. */
|
|
@@ -64,6 +62,7 @@ const DefaultInput = ({
|
|
|
64
62
|
const { l } = usePage();
|
|
65
63
|
const [firstFocus, setFirstFocus] = useState(true);
|
|
66
64
|
const validateResult = validate ? validate(value) : undefined;
|
|
65
|
+
const inputBase = (useUiRecipe("input") ?? inputRecipe)();
|
|
67
66
|
const status: "error" | "warning" | "success" | null =
|
|
68
67
|
!nullable && !value ? null : !value.length ? "warning" : validateResult === true ? "success" : "error";
|
|
69
68
|
const invalidMessage =
|
|
@@ -110,6 +109,7 @@ const DefaultInput = ({
|
|
|
110
109
|
{icon ? <div className={cn("flex items-center justify-center", iconClassName)}>{icon}</div> : null}
|
|
111
110
|
<input
|
|
112
111
|
{...rest}
|
|
112
|
+
{...agentAttrs(onChange)}
|
|
113
113
|
ref={inputRef}
|
|
114
114
|
value={value}
|
|
115
115
|
onChange={(e) => {
|
|
@@ -169,6 +169,7 @@ const DefaultTextArea = ({
|
|
|
169
169
|
const { l } = usePage();
|
|
170
170
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
171
171
|
const validateResult = validate(value);
|
|
172
|
+
const textareaBase = (useUiRecipe("input") ?? inputRecipe)({ kind: "area" });
|
|
172
173
|
const [firstFocus, setFirstFocus] = useState(true);
|
|
173
174
|
const status: "error" | "warning" | "success" =
|
|
174
175
|
!nullable && !value.length ? "warning" : validateResult === true ? "success" : "error";
|
|
@@ -202,6 +203,7 @@ const DefaultTextArea = ({
|
|
|
202
203
|
<div className={cn("relative mb-5", className)}>
|
|
203
204
|
<textarea
|
|
204
205
|
{...rest}
|
|
206
|
+
{...agentAttrs(onChange)}
|
|
205
207
|
ref={inputRef}
|
|
206
208
|
value={value}
|
|
207
209
|
onChange={(e) => {
|
|
@@ -263,6 +265,7 @@ const DefaultPassword = ({
|
|
|
263
265
|
const validateResult = validate(value);
|
|
264
266
|
const [firstFocus, setFirstFocus] = useState(true);
|
|
265
267
|
const [showPassword, setShowPassword] = useState(false);
|
|
268
|
+
const inputBase = (useUiRecipe("input") ?? inputRecipe)();
|
|
266
269
|
const status: "error" | "warning" | "success" =
|
|
267
270
|
!nullable && !value.length ? "warning" : validateResult === true ? "success" : "error";
|
|
268
271
|
const invalidMessage =
|
|
@@ -301,6 +304,7 @@ const DefaultPassword = ({
|
|
|
301
304
|
) : null}
|
|
302
305
|
<input
|
|
303
306
|
{...rest}
|
|
307
|
+
{...agentAttrs(onChange)}
|
|
304
308
|
type={showPassword ? "text" : "password"}
|
|
305
309
|
ref={inputRef}
|
|
306
310
|
value={value}
|
|
@@ -373,6 +377,7 @@ const DefaultEmail = ({
|
|
|
373
377
|
const { l } = usePage();
|
|
374
378
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
375
379
|
const isValidEmail = isEmail(value);
|
|
380
|
+
const inputBase = (useUiRecipe("input") ?? inputRecipe)();
|
|
376
381
|
const [firstFocus, setFirstFocus] = useState(true);
|
|
377
382
|
const validateResult = !isValidEmail ? l("base.emailInvalidError") : validate(value);
|
|
378
383
|
const status: "error" | "warning" | "success" =
|
|
@@ -418,6 +423,7 @@ const DefaultEmail = ({
|
|
|
418
423
|
) : null}
|
|
419
424
|
<input
|
|
420
425
|
{...rest}
|
|
426
|
+
{...agentAttrs(onChange)}
|
|
421
427
|
type="email"
|
|
422
428
|
value={value}
|
|
423
429
|
ref={inputRef}
|
|
@@ -484,6 +490,7 @@ const DefaultNumber = ({
|
|
|
484
490
|
const { l } = usePage();
|
|
485
491
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
486
492
|
const validateResult = validate ? validate(value) : undefined;
|
|
493
|
+
const inputBase = (useUiRecipe("input") ?? inputRecipe)();
|
|
487
494
|
const generateFormat = () => {
|
|
488
495
|
return isNaN(value ?? 0) ? "" : formatter ? formatter(value?.toString() ?? "") : (value?.toString() ?? "");
|
|
489
496
|
};
|
|
@@ -584,6 +591,7 @@ const DefaultNumber = ({
|
|
|
584
591
|
) : null}
|
|
585
592
|
<input
|
|
586
593
|
{...rest}
|
|
594
|
+
{...agentAttrs(onChange)}
|
|
587
595
|
ref={inputRef}
|
|
588
596
|
value={formatValue}
|
|
589
597
|
onKeyDown={handleKeyDown}
|
|
@@ -621,9 +629,10 @@ const DefaultCheckbox = ({ checked, onChange, className, ...rest }: CheckboxProp
|
|
|
621
629
|
return (
|
|
622
630
|
<input
|
|
623
631
|
{...rest}
|
|
632
|
+
{...agentAttrs(onChange)}
|
|
624
633
|
type="checkbox"
|
|
625
634
|
checked={checked}
|
|
626
|
-
className={cn("
|
|
635
|
+
className={cn("size-5 accent-primary", className)}
|
|
627
636
|
onChange={(e) => {
|
|
628
637
|
onChange(e.target.checked, e);
|
|
629
638
|
}}
|
|
@@ -9,6 +9,7 @@ export interface BottomInsetProps {
|
|
|
9
9
|
className?: string;
|
|
10
10
|
children: ReactNode;
|
|
11
11
|
keyboardSticky?: boolean;
|
|
12
|
+
contentAnchor?: "bottom";
|
|
12
13
|
role?: "bottomChrome" | "keyboardAccessory";
|
|
13
14
|
estimatedHeight?: number;
|
|
14
15
|
frameScope?: FrameSlotRegistration["scope"];
|
|
@@ -20,6 +21,7 @@ export const BottomInset = ({
|
|
|
20
21
|
className,
|
|
21
22
|
children,
|
|
22
23
|
keyboardSticky,
|
|
24
|
+
contentAnchor,
|
|
23
25
|
role,
|
|
24
26
|
estimatedHeight,
|
|
25
27
|
frameScope = "page",
|
|
@@ -49,12 +51,13 @@ export const BottomInset = ({
|
|
|
49
51
|
debugFrame("bottomInset.mount", {
|
|
50
52
|
path,
|
|
51
53
|
keyboardSticky,
|
|
54
|
+
contentAnchor,
|
|
52
55
|
role: frameRole,
|
|
53
56
|
frameScope,
|
|
54
57
|
frameSource,
|
|
55
58
|
});
|
|
56
59
|
return () => debugFrame("bottomInset.unmount", { path, frameSource });
|
|
57
|
-
}, [path, keyboardSticky, frameRole, frameScope, frameSource]);
|
|
60
|
+
}, [path, keyboardSticky, contentAnchor, frameRole, frameScope, frameSource]);
|
|
58
61
|
useLayoutEffect(() => {
|
|
59
62
|
if (!path) return;
|
|
60
63
|
const height = useDeclaredHeight ? resolvedEstimatedHeight : (measuredHeight ?? readContentHeight());
|
|
@@ -63,6 +66,7 @@ export const BottomInset = ({
|
|
|
63
66
|
role: frameRole,
|
|
64
67
|
scope: frameScope,
|
|
65
68
|
source: frameSource,
|
|
69
|
+
contentAnchor,
|
|
66
70
|
estimatedHeight: resolvedEstimatedHeight,
|
|
67
71
|
height,
|
|
68
72
|
cache: frameCache,
|
|
@@ -72,6 +76,7 @@ export const BottomInset = ({
|
|
|
72
76
|
frameRole,
|
|
73
77
|
frameScope,
|
|
74
78
|
frameSource,
|
|
79
|
+
contentAnchor,
|
|
75
80
|
resolvedEstimatedHeight,
|
|
76
81
|
measuredHeight,
|
|
77
82
|
frameCache,
|
package/ui/Load/Units.tsx
CHANGED
|
@@ -3,10 +3,10 @@ import { DataList } from "akanjs/base";
|
|
|
3
3
|
import { cn } from "akanjs/client";
|
|
4
4
|
import { capitalize, isQueryEqual, lowerlize } from "akanjs/common";
|
|
5
5
|
import type { BaseInsight } from "akanjs/constant";
|
|
6
|
-
import { ConstantRegistry } from "akanjs/constant";
|
|
6
|
+
import { ConstantRegistry, labelOf } from "akanjs/constant";
|
|
7
7
|
import type { ClientInit, ServerInit } from "akanjs/fetch";
|
|
8
8
|
import { st } from "akanjs/store";
|
|
9
|
-
import { useFetch } from "akanjs/webkit";
|
|
9
|
+
import { useFetch, useScreenScope } from "akanjs/webkit";
|
|
10
10
|
import { type ReactNode, type RefObject, useEffect, useMemo, useRef } from "react";
|
|
11
11
|
|
|
12
12
|
import { Empty } from "../Empty";
|
|
@@ -183,6 +183,15 @@ function Render<RefName extends string, Light extends { id: string }>({
|
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
const modelDataList = !loaded.current ? modelInitList.filter(filter).sort(sort) : modelList.filter(filter).sort(sort);
|
|
186
|
+
useScreenScope({
|
|
187
|
+
id: sliceName,
|
|
188
|
+
kind: refName,
|
|
189
|
+
items: () =>
|
|
190
|
+
modelDataList.map((item) => {
|
|
191
|
+
const label = labelOf(cnst.full, item);
|
|
192
|
+
return { id: item.id, ...(label ? { label } : {}) };
|
|
193
|
+
}),
|
|
194
|
+
});
|
|
186
195
|
const showLoading = loaded.current && modelListLoading;
|
|
187
196
|
if (renderList)
|
|
188
197
|
return (
|
package/ui/Load/View.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "akanjs/client";
|
|
3
|
-
import { ConstantRegistry } from "akanjs/constant";
|
|
3
|
+
import { ConstantRegistry, labelOf } from "akanjs/constant";
|
|
4
4
|
import type { ClientView, ServerView } from "akanjs/fetch";
|
|
5
5
|
import { st } from "akanjs/store";
|
|
6
|
-
import { useFetch } from "akanjs/webkit";
|
|
6
|
+
import { useFetch, useScreenScope } from "akanjs/webkit";
|
|
7
7
|
import { type ReactNode, useEffect, useMemo, useRef } from "react";
|
|
8
8
|
|
|
9
9
|
import { Empty } from "../Empty";
|
|
@@ -72,6 +72,11 @@ function Render<T extends string, Full extends { id: string }>({
|
|
|
72
72
|
}, [modelViewAt, modelObj.id]);
|
|
73
73
|
|
|
74
74
|
const renderModel = loadedId.current === modelObj.id ? model : modelInit;
|
|
75
|
+
useScreenScope({
|
|
76
|
+
id: `${refName}-view`,
|
|
77
|
+
kind: refName,
|
|
78
|
+
label: renderModel ? labelOf(cnst.full, renderModel) : undefined,
|
|
79
|
+
});
|
|
75
80
|
|
|
76
81
|
return noDiv && renderModel ? (
|
|
77
82
|
<>{renderView(renderModel)}</>
|
|
@@ -10,5 +10,12 @@ export interface ProgressBarProps {
|
|
|
10
10
|
}
|
|
11
11
|
export const ProgressBar = ({ className, value, max }: ProgressBarProps) => {
|
|
12
12
|
const progress = useSpring({ value: 0, to: { value: value } });
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<animated.progress
|
|
16
|
+
className={cn("h-2 w-full rounded-box accent-primary", className)}
|
|
17
|
+
value={progress.value}
|
|
18
|
+
max={max}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
14
21
|
};
|
package/ui/Menu.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import { type ReactNode, useCallback, useEffect, useLayoutEffect, useRef, useSta
|
|
|
5
5
|
import { AiFillCaretDown, AiOutlineEllipsis } from "react-icons/ai";
|
|
6
6
|
import { buttonRecipe } from "./Button";
|
|
7
7
|
|
|
8
|
-
import { createOverridable } from "./UiOverride";
|
|
8
|
+
import { createOverridable, useUiRecipe } from "./UiOverride";
|
|
9
9
|
|
|
10
10
|
export interface MenuItem {
|
|
11
11
|
label: ReactNode;
|
|
@@ -62,13 +62,11 @@ export const DefaultMenu = ({
|
|
|
62
62
|
? `fixed bottom-0 flex translate-y-[98%] flex-col rounded-xs border-0 bg-background shadow-lg hover:bg-background`
|
|
63
63
|
: "flex flex-col gap-0 p-0 bg-primary/10 hover:bg-primary/10 overflow-hidden";
|
|
64
64
|
|
|
65
|
+
const recipe = useUiRecipe("button") ?? buttonRecipe;
|
|
65
66
|
const subMenuItemClassName =
|
|
66
67
|
mode === "inline"
|
|
67
|
-
?
|
|
68
|
-
:
|
|
69
|
-
{ variant: "ghost" },
|
|
70
|
-
"w-full whitespace-nowrap text-center font-normal text-primary duration-300",
|
|
71
|
-
);
|
|
68
|
+
? recipe({ variant: "ghost" }, "m-0 h-full w-full px-2 font-normal text-primary hover:bg-primary/20")
|
|
69
|
+
: recipe({ variant: "ghost" }, "w-full whitespace-nowrap text-center font-normal text-primary duration-300");
|
|
72
70
|
|
|
73
71
|
const [overflowMenuItems, setOverflowMenuItems] = useState<MenuItem[]>([]);
|
|
74
72
|
|
|
@@ -222,6 +220,7 @@ interface OverflowMenuProps {
|
|
|
222
220
|
const OverflowMenu = ({ overflowItems, onClick }: OverflowMenuProps) => {
|
|
223
221
|
const [isOpen, setIsOpen] = useState(false);
|
|
224
222
|
const [expandedKey, setExpandedKey] = useState<string>();
|
|
223
|
+
const recipe = useUiRecipe("button") ?? buttonRecipe;
|
|
225
224
|
const handleMouseEnter = () => {
|
|
226
225
|
setIsOpen(true);
|
|
227
226
|
};
|
|
@@ -239,7 +238,7 @@ const OverflowMenu = ({ overflowItems, onClick }: OverflowMenuProps) => {
|
|
|
239
238
|
<div
|
|
240
239
|
key={item.key}
|
|
241
240
|
onClick={() => onClick?.(item)}
|
|
242
|
-
className={
|
|
241
|
+
className={recipe({ variant: "ghost" }, "relative whitespace-nowrap font-normal text-primary")}
|
|
243
242
|
onMouseEnter={() => {
|
|
244
243
|
if (item.children && expandedKey !== item.key) setExpandedKey(item.key);
|
|
245
244
|
}}
|
|
@@ -258,7 +257,7 @@ const OverflowMenu = ({ overflowItems, onClick }: OverflowMenuProps) => {
|
|
|
258
257
|
setExpandedKey(undefined);
|
|
259
258
|
}}
|
|
260
259
|
key={child.key}
|
|
261
|
-
className={
|
|
260
|
+
className={recipe(
|
|
262
261
|
{ variant: "ghost" },
|
|
263
262
|
"flex items-center justify-center text-center font-normal text-primary",
|
|
264
263
|
)}
|
package/ui/Model/EditModal.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import { ConstantRegistry, immerify } from "akanjs/constant";
|
|
|
5
5
|
import type { ClientEdit, ServerEdit, SliceMeta } from "akanjs/fetch";
|
|
6
6
|
import { type CreateOption, type Submit, st } from "akanjs/store";
|
|
7
7
|
import { useDebounce } from "akanjs/webkit";
|
|
8
|
-
import { type ReactNode, type Usable, use, useCallback, useEffect, useMemo } from "react";
|
|
8
|
+
import { type ReactNode, type Usable, use, useCallback, useEffect, useMemo, useRef } from "react";
|
|
9
9
|
import { AiOutlinePlus, AiOutlineSave } from "react-icons/ai";
|
|
10
10
|
|
|
11
11
|
import { Button } from "../Button";
|
|
@@ -13,6 +13,10 @@ import { Modal } from "../Modal";
|
|
|
13
13
|
|
|
14
14
|
const EDIT_PAYLOAD_MAX_AGE_MS = 60_000;
|
|
15
15
|
|
|
16
|
+
const pendingModalResets = new Map<string, { cancel: () => void }>();
|
|
17
|
+
const modalResetKey = (modelName: string, modalName: string, modalId?: string) =>
|
|
18
|
+
`${modelName}|${modalName}|${modalId ?? ""}`;
|
|
19
|
+
|
|
16
20
|
interface EditModelProps<Full> {
|
|
17
21
|
/** Rendering mode for the edit shell. */
|
|
18
22
|
type?: "modal" | "form" | "empty";
|
|
@@ -202,9 +206,40 @@ export default function EditModal<Full extends { id: string }>({
|
|
|
202
206
|
const crystal = new modelRef().set(modelEdit as Full) as unknown as Full;
|
|
203
207
|
void storeDo[names.newModel](crystal, { modal, setDefault: true, sliceName });
|
|
204
208
|
}
|
|
209
|
+
}, [modelEdit, isEditPayloadStale]);
|
|
210
|
+
|
|
211
|
+
const ownershipRef = useRef({ wasOpen: false, modalName: modal ?? "edit", modalId });
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
ownershipRef.current = {
|
|
214
|
+
wasOpen: ownershipRef.current.wasOpen || isModalOpen,
|
|
215
|
+
modalName: modal ?? "edit",
|
|
216
|
+
modalId,
|
|
217
|
+
};
|
|
218
|
+
}, [isModalOpen, modal, modalId]);
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
pendingModalResets.get(modalResetKey(modelName, modal ?? "edit", modalId))?.cancel();
|
|
205
221
|
return () => {
|
|
222
|
+
|
|
223
|
+
const { wasOpen, modalName, modalId } = ownershipRef.current;
|
|
224
|
+
if (!wasOpen) return;
|
|
225
|
+
const key = modalResetKey(modelName, modalName, modalId);
|
|
226
|
+
let cancelled = false;
|
|
227
|
+
pendingModalResets.set(key, {
|
|
228
|
+
cancel: () => {
|
|
229
|
+
cancelled = true;
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
queueMicrotask(() => {
|
|
233
|
+
pendingModalResets.delete(key);
|
|
234
|
+
if (cancelled) return;
|
|
235
|
+
const state = st.get() as unknown as { [key: string]: unknown };
|
|
236
|
+
if (state[names.modelModal] !== modalName) return;
|
|
237
|
+
const formId = (state[names.modelForm] as { id: string | null } | undefined)?.id ?? null;
|
|
238
|
+
if (formId !== (modalId ?? null)) return;
|
|
239
|
+
void storeDo[names.setModelModal](null);
|
|
240
|
+
});
|
|
206
241
|
};
|
|
207
|
-
}, [
|
|
242
|
+
}, []);
|
|
208
243
|
|
|
209
244
|
const handleCancel = useCallback(() => {
|
|
210
245
|
const modelForm = (st.get() as any)[names.modelForm] as Full;
|