@principal-ai/file-city-react 0.5.40 → 0.5.41
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/dist/components/FileCity3D/FileCity3D.d.ts +8 -2
- package/dist/components/FileCity3D/FileCity3D.d.ts.map +1 -1
- package/dist/components/FileCity3D/FileCity3D.js +129 -40
- package/dist/components/FileCityExplorer/AddToAreaModal.d.ts +14 -0
- package/dist/components/FileCityExplorer/AddToAreaModal.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/AddToAreaModal.js +140 -0
- package/dist/components/FileCityExplorer/AddToScopeModal.d.ts +14 -0
- package/dist/components/FileCityExplorer/AddToScopeModal.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/AddToScopeModal.js +176 -0
- package/dist/components/FileCityExplorer/FileCityExplorer.d.ts +30 -0
- package/dist/components/FileCityExplorer/FileCityExplorer.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/FileCityExplorer.js +1045 -0
- package/dist/components/FileCityExplorer/ScopeInfoOverlay.d.ts +10 -0
- package/dist/components/FileCityExplorer/ScopeInfoOverlay.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/ScopeInfoOverlay.js +73 -0
- package/dist/components/FileCityExplorer/index.d.ts +3 -0
- package/dist/components/FileCityExplorer/index.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/index.js +1 -0
- package/dist/components/FileCityExplorer/layers.d.ts +16 -0
- package/dist/components/FileCityExplorer/layers.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/layers.js +61 -0
- package/dist/components/FileCityExplorer/model.d.ts +32 -0
- package/dist/components/FileCityExplorer/model.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/model.js +14 -0
- package/dist/components/FileCityExplorer/pathConversion.d.ts +19 -0
- package/dist/components/FileCityExplorer/pathConversion.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/pathConversion.js +26 -0
- package/dist/components/FileCityExplorer/scopeTreePaths.d.ts +21 -0
- package/dist/components/FileCityExplorer/scopeTreePaths.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/scopeTreePaths.js +42 -0
- package/dist/components/FileCityExplorer/styles.d.ts +9 -0
- package/dist/components/FileCityExplorer/styles.d.ts.map +1 -0
- package/dist/components/FileCityExplorer/styles.js +28 -0
- package/dist/utils/folderElevatedPanels.d.ts +3 -1
- package/dist/utils/folderElevatedPanels.d.ts.map +1 -1
- package/dist/utils/folderElevatedPanels.js +5 -2
- package/package.json +2 -1
- package/src/components/FileCity3D/FileCity3D.tsx +200 -52
- package/src/components/FileCityExplorer/AddToAreaModal.tsx +273 -0
- package/src/components/FileCityExplorer/AddToScopeModal.tsx +320 -0
- package/src/components/FileCityExplorer/FileCityExplorer.tsx +1457 -0
- package/src/components/FileCityExplorer/ScopeInfoOverlay.tsx +229 -0
- package/src/components/FileCityExplorer/index.ts +2 -0
- package/src/components/FileCityExplorer/layers.ts +72 -0
- package/src/components/FileCityExplorer/model.ts +35 -0
- package/src/components/FileCityExplorer/pathConversion.ts +32 -0
- package/src/components/FileCityExplorer/scopeTreePaths.ts +52 -0
- package/src/components/FileCityExplorer/styles.ts +34 -0
- package/src/stories/2D3DComparison.stories.tsx +13 -2
- package/src/stories/ElevatedScopePanels.stories.tsx +295 -0
- package/src/stories/FileCity3D.stories.tsx +24 -3
- package/src/stories/FileCityExplorer.stories.tsx +2474 -0
- package/src/stories/FileCityExplorerComponent.stories.tsx +59 -0
- package/src/utils/folderElevatedPanels.ts +8 -2
- package/src/stories/ScopeOverlay.stories.tsx +0 -1610
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { useTheme } from '@principal-ade/industry-theme';
|
|
4
|
+
import { makeSectionLabelStyle } from './styles';
|
|
5
|
+
export const AddToScopeModal = ({ path, scopes, scopeId, namespaceName, onScopeIdChange, onNamespaceNameChange, onPickExisting, onSubmit, onClose, }) => {
|
|
6
|
+
const { theme } = useTheme();
|
|
7
|
+
const sectionLabelStyle = makeSectionLabelStyle(theme);
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const onKey = (e) => {
|
|
10
|
+
if (e.key === 'Escape')
|
|
11
|
+
onClose();
|
|
12
|
+
};
|
|
13
|
+
window.addEventListener('keydown', onKey);
|
|
14
|
+
return () => window.removeEventListener('keydown', onKey);
|
|
15
|
+
}, [onClose]);
|
|
16
|
+
const trimmedScope = scopeId.trim();
|
|
17
|
+
const trimmedNs = namespaceName.trim();
|
|
18
|
+
const canSubmit = trimmedScope.length > 0;
|
|
19
|
+
// Determine what the submit will do, for the action label.
|
|
20
|
+
const targetScope = scopes.find(s => s.id === trimmedScope);
|
|
21
|
+
const targetNs = trimmedNs
|
|
22
|
+
? targetScope?.namespaces.find(n => n.name === trimmedNs) ?? null
|
|
23
|
+
: null;
|
|
24
|
+
const alreadyClaimed = trimmedNs
|
|
25
|
+
? targetNs?.paths.includes(path) ?? false
|
|
26
|
+
: targetScope?.paths.includes(path) ?? false;
|
|
27
|
+
let actionLabel = 'Add';
|
|
28
|
+
if (alreadyClaimed)
|
|
29
|
+
actionLabel = 'Already added';
|
|
30
|
+
else if (!targetScope && !trimmedNs)
|
|
31
|
+
actionLabel = 'Create scope';
|
|
32
|
+
else if (!targetScope)
|
|
33
|
+
actionLabel = 'Create scope + namespace';
|
|
34
|
+
else if (!trimmedNs)
|
|
35
|
+
actionLabel = 'Add to scope';
|
|
36
|
+
else if (!targetNs)
|
|
37
|
+
actionLabel = 'Create namespace';
|
|
38
|
+
else
|
|
39
|
+
actionLabel = 'Add path';
|
|
40
|
+
const sectionDivider = `1px solid ${theme.colors.backgroundSecondary}`;
|
|
41
|
+
const inputStyle = {
|
|
42
|
+
padding: '8px 10px',
|
|
43
|
+
background: theme.colors.backgroundDark ?? theme.colors.background,
|
|
44
|
+
color: theme.colors.text,
|
|
45
|
+
border: `1px solid ${theme.colors.border}`,
|
|
46
|
+
borderRadius: theme.radii[2],
|
|
47
|
+
fontFamily: theme.fonts.monospace,
|
|
48
|
+
fontSize: theme.fontSizes[1],
|
|
49
|
+
};
|
|
50
|
+
return (_jsx("div", { onClick: onClose, style: {
|
|
51
|
+
position: 'fixed',
|
|
52
|
+
inset: 0,
|
|
53
|
+
background: 'rgba(0, 0, 0, 0.55)',
|
|
54
|
+
display: 'flex',
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
justifyContent: 'center',
|
|
57
|
+
zIndex: 1000,
|
|
58
|
+
fontFamily: theme.fonts.body,
|
|
59
|
+
}, children: _jsxs("div", { onClick: e => e.stopPropagation(), style: {
|
|
60
|
+
width: 520,
|
|
61
|
+
maxHeight: 'min(80vh, 700px)',
|
|
62
|
+
display: 'flex',
|
|
63
|
+
flexDirection: 'column',
|
|
64
|
+
background: theme.colors.background,
|
|
65
|
+
color: theme.colors.text,
|
|
66
|
+
borderRadius: theme.radii[4],
|
|
67
|
+
border: `1px solid ${theme.colors.border}`,
|
|
68
|
+
boxShadow: theme.shadows[4],
|
|
69
|
+
overflow: 'hidden',
|
|
70
|
+
}, children: [_jsxs("div", { style: {
|
|
71
|
+
padding: '14px 18px',
|
|
72
|
+
borderBottom: sectionDivider,
|
|
73
|
+
display: 'flex',
|
|
74
|
+
justifyContent: 'space-between',
|
|
75
|
+
alignItems: 'flex-start',
|
|
76
|
+
gap: theme.space[3],
|
|
77
|
+
}, children: [_jsxs("div", { children: [_jsx("div", { style: sectionLabelStyle, children: "Add to scope" }), _jsx("div", { style: {
|
|
78
|
+
fontFamily: theme.fonts.monospace,
|
|
79
|
+
fontSize: theme.fontSizes[0],
|
|
80
|
+
color: theme.colors.textMuted,
|
|
81
|
+
marginTop: 6,
|
|
82
|
+
wordBreak: 'break-all',
|
|
83
|
+
}, children: path })] }), _jsx("button", { onClick: onClose, style: {
|
|
84
|
+
background: 'transparent',
|
|
85
|
+
border: 'none',
|
|
86
|
+
color: theme.colors.textTertiary,
|
|
87
|
+
fontSize: theme.fontSizes[3],
|
|
88
|
+
cursor: 'pointer',
|
|
89
|
+
lineHeight: 1,
|
|
90
|
+
padding: 0,
|
|
91
|
+
}, "aria-label": "Close", children: "\u00D7" })] }), _jsxs("div", { style: {
|
|
92
|
+
padding: '14px 18px',
|
|
93
|
+
borderBottom: sectionDivider,
|
|
94
|
+
display: 'flex',
|
|
95
|
+
flexDirection: 'column',
|
|
96
|
+
gap: theme.space[3],
|
|
97
|
+
}, children: [_jsxs("label", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx("span", { style: sectionLabelStyle, children: "Scope" }), _jsx("input", { type: "text", value: scopeId, list: "scope-id-options", autoFocus: true, placeholder: "e.g. principal-view.cli", onChange: e => onScopeIdChange(e.target.value), onKeyDown: e => {
|
|
98
|
+
if (e.key === 'Enter' && canSubmit && !alreadyClaimed)
|
|
99
|
+
onSubmit();
|
|
100
|
+
}, style: inputStyle }), _jsx("datalist", { id: "scope-id-options", children: scopes.map(s => (_jsx("option", { value: s.id }, s.id))) })] }), _jsxs("label", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx("span", { style: sectionLabelStyle, children: "Namespace (optional)" }), _jsx("input", { type: "text", value: namespaceName, placeholder: "leave blank to add to scope itself", onChange: e => onNamespaceNameChange(e.target.value), onKeyDown: e => {
|
|
101
|
+
if (e.key === 'Enter' && canSubmit && !alreadyClaimed)
|
|
102
|
+
onSubmit();
|
|
103
|
+
}, style: inputStyle })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'flex-end', gap: theme.space[2] }, children: [_jsx("button", { onClick: onClose, style: {
|
|
104
|
+
padding: '8px 14px',
|
|
105
|
+
background: 'transparent',
|
|
106
|
+
color: theme.colors.textSecondary,
|
|
107
|
+
border: `1px solid ${theme.colors.border}`,
|
|
108
|
+
borderRadius: theme.radii[2],
|
|
109
|
+
cursor: 'pointer',
|
|
110
|
+
fontSize: theme.fontSizes[1],
|
|
111
|
+
}, children: "Cancel" }), _jsx("button", { onClick: onSubmit, disabled: !canSubmit || alreadyClaimed, style: {
|
|
112
|
+
padding: '8px 14px',
|
|
113
|
+
background: !canSubmit || alreadyClaimed
|
|
114
|
+
? theme.colors.backgroundSecondary
|
|
115
|
+
: theme.colors.primary,
|
|
116
|
+
color: !canSubmit || alreadyClaimed
|
|
117
|
+
? theme.colors.muted
|
|
118
|
+
: theme.colors.textOnPrimary,
|
|
119
|
+
border: `1px solid ${theme.colors.border}`,
|
|
120
|
+
borderRadius: theme.radii[2],
|
|
121
|
+
cursor: !canSubmit || alreadyClaimed ? 'not-allowed' : 'pointer',
|
|
122
|
+
fontSize: theme.fontSizes[1],
|
|
123
|
+
fontWeight: theme.fontWeights.medium,
|
|
124
|
+
}, children: actionLabel })] })] }), _jsxs("div", { style: {
|
|
125
|
+
padding: '14px 18px',
|
|
126
|
+
overflowY: 'auto',
|
|
127
|
+
flex: 1,
|
|
128
|
+
}, children: [_jsx("div", { style: sectionLabelStyle, children: "Existing scopes (click to prefill)" }), _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: theme.space[3], marginTop: theme.space[2] }, children: scopes.map(scope => (_jsxs("div", { children: [_jsx("div", { style: {
|
|
129
|
+
fontFamily: theme.fonts.monospace,
|
|
130
|
+
fontSize: theme.fontSizes[0],
|
|
131
|
+
color: theme.colors.textSecondary,
|
|
132
|
+
marginBottom: 6,
|
|
133
|
+
}, children: scope.id }), _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: theme.space[1] }, children: [_jsxs("button", { onClick: () => onPickExisting(scope.id, ''), title: scope.paths.includes(path)
|
|
134
|
+
? 'Scope already claims this path'
|
|
135
|
+
: 'Prefill (scope-level)', style: {
|
|
136
|
+
fontSize: theme.fontSizes[0],
|
|
137
|
+
padding: '3px 7px',
|
|
138
|
+
background: scope.paths.includes(path)
|
|
139
|
+
? theme.colors.background
|
|
140
|
+
: theme.colors.backgroundSecondary,
|
|
141
|
+
color: scope.paths.includes(path)
|
|
142
|
+
? theme.colors.muted
|
|
143
|
+
: theme.colors.textSecondary,
|
|
144
|
+
border: `1px dashed ${theme.colors.muted}`,
|
|
145
|
+
borderRadius: theme.radii[1],
|
|
146
|
+
cursor: 'pointer',
|
|
147
|
+
display: 'flex',
|
|
148
|
+
alignItems: 'center',
|
|
149
|
+
gap: 5,
|
|
150
|
+
fontStyle: 'italic',
|
|
151
|
+
opacity: scope.paths.includes(path) ? 0.6 : 1,
|
|
152
|
+
}, children: ["(scope-level)", scope.paths.includes(path) && (_jsx("span", { style: { marginLeft: theme.space[1], fontSize: theme.fontSizes[0] }, children: "\u2713" }))] }), scope.namespaces.map(ns => {
|
|
153
|
+
const claims = ns.paths.includes(path);
|
|
154
|
+
return (_jsxs("button", { onClick: () => onPickExisting(scope.id, ns.name), title: claims ? 'Already claims this path' : 'Prefill inputs', style: {
|
|
155
|
+
fontSize: theme.fontSizes[0],
|
|
156
|
+
padding: '3px 7px',
|
|
157
|
+
background: claims
|
|
158
|
+
? theme.colors.background
|
|
159
|
+
: theme.colors.backgroundSecondary,
|
|
160
|
+
color: claims ? theme.colors.muted : theme.colors.text,
|
|
161
|
+
border: `1px solid ${theme.colors.border}`,
|
|
162
|
+
borderRadius: theme.radii[1],
|
|
163
|
+
cursor: 'pointer',
|
|
164
|
+
display: 'flex',
|
|
165
|
+
alignItems: 'center',
|
|
166
|
+
gap: 5,
|
|
167
|
+
opacity: claims ? 0.6 : 1,
|
|
168
|
+
}, children: [_jsx("span", { style: {
|
|
169
|
+
width: 8,
|
|
170
|
+
height: 8,
|
|
171
|
+
borderRadius: theme.radii[1],
|
|
172
|
+
background: ns.color,
|
|
173
|
+
flexShrink: 0,
|
|
174
|
+
} }), ns.name, claims && _jsx("span", { style: { marginLeft: theme.space[1], fontSize: theme.fontSizes[0] }, children: "\u2713" })] }, ns.name));
|
|
175
|
+
})] })] }, scope.id))) })] })] }) }));
|
|
176
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type CityData } from '../FileCity3D';
|
|
3
|
+
import type { ProjectArea, Scope } from './model';
|
|
4
|
+
export interface FileCityExplorerProps {
|
|
5
|
+
/** City data to render in the 3D canvas. */
|
|
6
|
+
cityData: CityData;
|
|
7
|
+
/**
|
|
8
|
+
* Prefix that scopes/namespace paths are stripped of when read out of
|
|
9
|
+
* `cityData` (e.g. `'electron-app/'`). Pass `''` if the city data is already
|
|
10
|
+
* rooted at the project root.
|
|
11
|
+
*/
|
|
12
|
+
packageRoot: string;
|
|
13
|
+
/** Initial scopes (used when no persisted state is found). */
|
|
14
|
+
initialScopes?: Scope[];
|
|
15
|
+
/** Initial areas (used when no persisted state is found). */
|
|
16
|
+
initialAreas?: ProjectArea[];
|
|
17
|
+
/**
|
|
18
|
+
* When set, scopes/areas round-trip through `localStorage` under
|
|
19
|
+
* `${persistKey}.scopes` and `${persistKey}.areas`. When omitted, state is
|
|
20
|
+
* purely in-memory.
|
|
21
|
+
*/
|
|
22
|
+
persistKey?: string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Initial focused directory (city path). Defaults to the city root derived
|
|
25
|
+
* from `packageRoot` (with trailing slash stripped).
|
|
26
|
+
*/
|
|
27
|
+
initialFocusDirectory?: string | null;
|
|
28
|
+
}
|
|
29
|
+
export declare const FileCityExplorer: React.FC<FileCityExplorerProps>;
|
|
30
|
+
//# sourceMappingURL=FileCityExplorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileCityExplorer.d.ts","sourceRoot":"","sources":["../../../src/components/FileCityExplorer/FileCityExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,OAAO,EAEL,KAAK,QAAQ,EAGd,MAAM,eAAe,CAAC;AAOvB,OAAO,KAAK,EAAa,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAuB7D,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC;IACxB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAy2C5D,CAAC"}
|