Make application shell components package-native
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nodedc/ui-catalog",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -9,8 +9,8 @@
|
||||
"typecheck": "tsc -b --pretty false"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nodedc/ui-core": "0.3.0",
|
||||
"@nodedc/ui-react": "0.3.0",
|
||||
"@nodedc/ui-core": "0.4.0",
|
||||
"@nodedc/ui-react": "0.4.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg id="nodedc-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220.82 54.55"><defs><style>.cls-1{fill:#e2e1e1}.cls-2{fill:#dbdbdb;stroke:#dbdbdb;stroke-miterlimit:10;stroke-width:.75px}</style></defs><path class="cls-1" d="M52.8,23.61,46.92,33.76,41.05,23.61H52.8m18-10.39H23.06L46.92,54.55Z"/><polygon class="cls-1" points="31.28 33.13 18.11 10.34 75.73 10.34 62.59 33.13 74.28 33.13 93.22 0 0 0 19.61 33.13 31.28 33.13"/><path class="cls-2" d="M116.35,18.49V1h1.27l10.34,15V1h1.33V18.49H128l-10.34-15v15Z"/><path class="cls-2" d="M140.43,18.64c-4.79,0-8.16-3.72-8.16-8.89S135.64.86,140.43.86s8.17,3.72,8.17,8.89S145.25,18.64,140.43,18.64Zm0-1.25c4,0,6.79-3.17,6.79-7.64s-2.77-7.64-6.79-7.64-6.77,3.17-6.77,7.64S136.44,17.39,140.43,17.39Z"/><path class="cls-2" d="M151.6,18.49V1h5.1c5.54,0,8.79,3.42,8.79,8.74s-3.25,8.74-8.79,8.74ZM153,17.24h3.75c4.77,0,7.42-2.92,7.42-7.49s-2.65-7.49-7.42-7.49H153Z"/><path class="cls-2" d="M168.49,1h10.77V2.26h-9.42V8.93h7.89v1.25h-7.89v7.06h9.74v1.25H168.49Z"/><path class="cls-2" d="M188.88,18.49V1H194c5.54,0,8.79,3.42,8.79,8.74s-3.25,8.74-8.79,8.74Zm1.35-1.25H194c4.77,0,7.41-2.92,7.41-7.49S198.75,2.26,194,2.26h-3.75Z"/><path class="cls-2" d="M205.15,9.75c0-5.24,3.19-8.89,8.11-8.89a6.8,6.8,0,0,1,7.1,5.52h-1.43a5.54,5.54,0,0,0-5.74-4.27c-4.05,0-6.64,3.17-6.64,7.64s2.54,7.64,6.59,7.64a5.46,5.46,0,0,0,5.74-4.29h1.43c-.75,3.52-3.4,5.54-7.15,5.54C208.27,18.64,205.15,15.05,205.15,9.75Z"/></svg>
|
||||
<svg id="nodedc-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220.82 54.55"><defs><style>.cls-1{fill:#e2e1e1;}.cls-2{fill:#dbdbdb;stroke:#dbdbdb;stroke-miterlimit:10;stroke-width:0.75px;}</style></defs><path class="cls-1" d="M52.8,23.61,46.92,33.76,41.05,23.61H52.8m18-10.39H23.06L46.92,54.55Z"/><polygon class="cls-1" points="31.28 33.13 18.11 10.34 75.73 10.34 62.59 33.13 74.28 33.13 93.22 0 0 0 19.61 33.13 31.28 33.13"/><path class="cls-2" d="M116.35,18.49V1h1.27l10.34,15V1h1.33V18.49H128l-10.34-15v15Z"/><path class="cls-2" d="M140.43,18.64c-4.79,0-8.16-3.72-8.16-8.89S135.64.86,140.43.86s8.17,3.72,8.17,8.89S145.25,18.64,140.43,18.64Zm0-1.25c4,0,6.79-3.17,6.79-7.64s-2.77-7.64-6.79-7.64-6.77,3.17-6.77,7.64S136.44,17.39,140.43,17.39Z"/><path class="cls-2" d="M151.6,18.49V1h5.1c5.54,0,8.79,3.42,8.79,8.74s-3.25,8.74-8.79,8.74ZM153,17.24h3.75c4.77,0,7.42-2.92,7.42-7.49s-2.65-7.49-7.42-7.49H153Z"/><path class="cls-2" d="M168.49,1h10.77V2.26h-9.42V8.93h7.89v1.25h-7.89v7.06h9.74v1.25H168.49Z"/><path class="cls-2" d="M188.88,18.49V1H194c5.54,0,8.79,3.42,8.79,8.74s-3.25,8.74-8.79,8.74Zm1.35-1.25H194c4.77,0,7.41-2.92,7.41-7.49S198.75,2.26,194,2.26h-3.75Z"/><path class="cls-2" d="M205.15,9.75c0-5.24,3.19-8.89,8.11-8.89a6.8,6.8,0,0,1,7.1,5.52h-1.43a5.54,5.54,0,0,0-5.74-4.27c-4.05,0-6.64,3.17-6.64,7.64s2.54,7.64,6.59,7.64a5.46,5.46,0,0,0,5.74-4.29h1.43c-.75,3.52-3.4,5.54-7.15,5.54C208.27,18.64,205.15,15.05,205.15,9.75Z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -14,23 +14,28 @@ import {
|
||||
FieldFrame,
|
||||
GlassSurface,
|
||||
HeaderAvatar,
|
||||
HeaderNavigation,
|
||||
HeaderProfile,
|
||||
HeaderProfileButton,
|
||||
HeaderWorkspace,
|
||||
Icon,
|
||||
IconButton,
|
||||
Inspector,
|
||||
MediaSourceField,
|
||||
RangeControl,
|
||||
Select,
|
||||
SettingsCard,
|
||||
StatusBadge,
|
||||
Switch,
|
||||
TextAreaField,
|
||||
TextField,
|
||||
useApplicationWorkspace,
|
||||
Window,
|
||||
WindowFooterActions,
|
||||
type IconName,
|
||||
} from "@nodedc/ui-react";
|
||||
|
||||
type CatalogSection = "foundation" | "controls" | "windows" | "modals" | "inspector" | "icons";
|
||||
type CatalogSection = "foundation" | "controls" | "media" | "windows" | "modals" | "inspector" | "icons";
|
||||
|
||||
const engineAccentStyle = {
|
||||
"--nodedc-accent-rgb": "148 123 219",
|
||||
@@ -63,26 +68,32 @@ const sectionDefinitions: Record<CatalogSection, { eyebrow: string; title: strin
|
||||
description: "Кнопки, поля, selection, Engine-настройки и понятные статусы операций.",
|
||||
icon: "sliders",
|
||||
},
|
||||
media: {
|
||||
eyebrow: "03 / MEDIA & SETTINGS",
|
||||
title: "Медиа и настройки",
|
||||
description: "Общий CMS/Launcher-контракт файла, URL, превью и нейтральных карточек настроек.",
|
||||
icon: "image",
|
||||
},
|
||||
windows: {
|
||||
eyebrow: "03 / WINDOWS",
|
||||
eyebrow: "04 / WINDOWS",
|
||||
title: "Окна",
|
||||
description: "Обычные окна и modeless-панели с единым portal- и focus-контрактом.",
|
||||
icon: "apps",
|
||||
},
|
||||
modals: {
|
||||
eyebrow: "04 / MODALS",
|
||||
eyebrow: "05 / MODALS",
|
||||
title: "Модалки",
|
||||
description: "Создание, подтверждение и разрушительные действия в адаптивном формате.",
|
||||
icon: "clipboard",
|
||||
},
|
||||
inspector: {
|
||||
eyebrow: "05 / INSPECTOR",
|
||||
eyebrow: "06 / INSPECTOR",
|
||||
title: "Инспектор",
|
||||
description: "Точный новый Engine Environment Settings без legacy-инспекторов.",
|
||||
icon: "settings",
|
||||
},
|
||||
icons: {
|
||||
eyebrow: "06 / ICONS",
|
||||
eyebrow: "07 / ICONS",
|
||||
title: "Иконки",
|
||||
description: "Канонический общий набор по Launcher, SEO, BIM Viewer и новым участкам Engine.",
|
||||
icon: "grid",
|
||||
@@ -197,9 +208,8 @@ function Preview({ title, note, children, className }: {
|
||||
export function CatalogApp() {
|
||||
const [theme, setTheme] = useState<NodedcTheme>("dark");
|
||||
const [accentHex, setAccentHex] = useState("#c3ff66");
|
||||
const [guidelineOpen, setGuidelineOpen] = useState(false);
|
||||
const [activeSection, setActiveSection] = useState<CatalogSection | null>(null);
|
||||
const [panelExpanded, setPanelExpanded] = useState(true);
|
||||
const workspace = useApplicationWorkspace<CatalogSection>();
|
||||
const { navigationOpen: guidelineOpen, activeView: activeSection, contentExpanded: panelExpanded } = workspace;
|
||||
const [selectedStatus, setSelectedStatus] = useState<(typeof selectOptions)[number]["value"]>("active");
|
||||
const [checker, setChecker] = useState(true);
|
||||
const [brightness, setBrightness] = useState(49);
|
||||
@@ -216,6 +226,10 @@ export function CatalogApp() {
|
||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||
const [projectName, setProjectName] = useState("NODE.DC Platform");
|
||||
const [notes, setNotes] = useState("Общий контракт компонентов без доменной логики приложения.");
|
||||
const [mediaSource, setMediaSource] = useState<"file" | "url">("file");
|
||||
const [mediaUrl, setMediaUrl] = useState("");
|
||||
const [mediaFileName, setMediaFileName] = useState("hub_vitrina-mqv7yd7k.mov");
|
||||
const [mediaVisible, setMediaVisible] = useState(true);
|
||||
|
||||
const accent = useMemo(() => rgbFromHex(accentHex) ?? accents[0].value, [accentHex]);
|
||||
|
||||
@@ -224,16 +238,10 @@ export function CatalogApp() {
|
||||
}, [accent, theme]);
|
||||
|
||||
const openSection = (next: string) => {
|
||||
const section = next as CatalogSection;
|
||||
setGuidelineOpen(true);
|
||||
setActiveSection(section);
|
||||
setPanelExpanded(true);
|
||||
workspace.openView(next as CatalogSection);
|
||||
};
|
||||
|
||||
const closeGuideline = () => {
|
||||
setGuidelineOpen(false);
|
||||
setActiveSection(null);
|
||||
};
|
||||
const closeGuideline = workspace.closeNavigation;
|
||||
|
||||
const inspectorSections = [
|
||||
{
|
||||
@@ -403,6 +411,38 @@ export function CatalogApp() {
|
||||
</Preview>
|
||||
</div>
|
||||
);
|
||||
case "media":
|
||||
return (
|
||||
<div className="catalog-grid catalog-grid--single">
|
||||
<SettingsCard
|
||||
eyebrow="CONTENT"
|
||||
title="Видео-окно"
|
||||
description="Окно демо-видео и подключённый медиа-файл. Хранилище и загрузка остаются в приложении; геометрия и переключение источника — в библиотеке."
|
||||
actions={<Switch checked={mediaVisible} label="Показать" onChange={setMediaVisible} />}
|
||||
>
|
||||
<MediaSourceField
|
||||
label="Видео / картинка"
|
||||
source={mediaSource}
|
||||
url={mediaUrl}
|
||||
fileName={mediaFileName}
|
||||
previewSrc={mediaSource === "url" && mediaUrl ? mediaUrl : "/launcher-stage-poster.png"}
|
||||
previewKind={mediaSource === "file" ? "image" : undefined}
|
||||
path="content.videoSrc → assets/uploads/media/component-library"
|
||||
hint="Адаптер приложения сохраняет файл и возвращает URL; компонент не знает API storage."
|
||||
onSourceChange={setMediaSource}
|
||||
onUrlChange={setMediaUrl}
|
||||
onFileChange={(file) => {
|
||||
if (file) setMediaFileName(file.name);
|
||||
}}
|
||||
/>
|
||||
</SettingsCard>
|
||||
<Preview title="Граница ответственности" note="component / adapter" className="catalog-preview--wide">
|
||||
<p className="catalog-preview__explanation">
|
||||
`MediaSourceField` владеет пикселями, доступностью и состояниями file/URL. Конкретный проект передаёт upload-обработчик, storage-path и результат валидации — поэтому компонент можно подключить к CMS, Launcher или будущему приложению без копирования его вёрстки.
|
||||
</p>
|
||||
</Preview>
|
||||
</div>
|
||||
);
|
||||
case "windows":
|
||||
return (
|
||||
<div className="catalog-grid catalog-grid--single">
|
||||
@@ -413,7 +453,7 @@ export function CatalogApp() {
|
||||
<Button shape="pill" icon={<Icon name="panel" />} onClick={() => setInspectorOpen(true)}>Открыть инспектор</Button>
|
||||
</div>
|
||||
</Preview>
|
||||
<Preview title="Оконные действия" note="круг `46.72 px`">
|
||||
<Preview title="Оконные действия" note="круг `46 px`">
|
||||
<div className="catalog-window-actions-demo">
|
||||
<Button shape="pill" icon={<Icon name="refresh" />}>Обновить источник</Button>
|
||||
<IconButton label="Добавить"><Icon name="plus" /></IconButton>
|
||||
@@ -485,7 +525,7 @@ export function CatalogApp() {
|
||||
className="catalog-app nodedc-ui-root"
|
||||
style={{ "--catalog-accent": accentHex } as CSSProperties}
|
||||
navigationOpen={guidelineOpen}
|
||||
contentOpen={Boolean(activeSection)}
|
||||
contentOpen={workspace.contentOpen}
|
||||
contentExpanded={panelExpanded}
|
||||
header={
|
||||
<AppHeader
|
||||
@@ -493,24 +533,19 @@ export function CatalogApp() {
|
||||
brandHref="/"
|
||||
center={
|
||||
<>
|
||||
<HeaderWorkspace className="catalog-header-workspace" label="NODE.DC Design" imageUrl="/nodedc-mark.svg" />
|
||||
<nav className="catalog-header-nav" aria-label="Навигация приложения">
|
||||
<button
|
||||
className="nodedc-header__nav-item"
|
||||
type="button"
|
||||
data-active={guidelineOpen ? "true" : undefined}
|
||||
aria-expanded={guidelineOpen}
|
||||
onClick={() => guidelineOpen ? closeGuideline() : setGuidelineOpen(true)}
|
||||
>
|
||||
Guideline
|
||||
</button>
|
||||
</nav>
|
||||
<HeaderWorkspace kind="mark" label="NODE.DC Design" imageUrl="/nodedc-mark.svg" />
|
||||
<HeaderNavigation
|
||||
label="Навигация приложения"
|
||||
value={guidelineOpen ? "guideline" : undefined}
|
||||
items={[{ value: "guideline", label: "Guideline" }]}
|
||||
onChange={() => guidelineOpen ? closeGuideline() : workspace.openNavigation()}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
right={
|
||||
<HeaderProfile>
|
||||
<IconButton label="Уведомления"><Icon name="inbox" /></IconButton>
|
||||
<HeaderProfileButton><Icon name="profile" size={17} />Профиль</HeaderProfileButton>
|
||||
<IconButton label="Уведомления"><Icon name="inbox" size={20} /></IconButton>
|
||||
<HeaderProfileButton>Профиль</HeaderProfileButton>
|
||||
<HeaderAvatar label="DC" />
|
||||
</HeaderProfile>
|
||||
}
|
||||
@@ -546,7 +581,7 @@ export function CatalogApp() {
|
||||
footer={
|
||||
<>
|
||||
<span className="nodedc-admin-panel__nav-icon" aria-hidden="true"><Icon name="shield" /></span>
|
||||
<span>Design System 0.3</span>
|
||||
<span>Design System 0.4</span>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
@@ -558,8 +593,8 @@ export function CatalogApp() {
|
||||
title={activeDefinition.title}
|
||||
description={activeDefinition.description}
|
||||
expanded={panelExpanded}
|
||||
onExpandedChange={setPanelExpanded}
|
||||
onClose={() => setActiveSection(null)}
|
||||
onExpandedChange={workspace.setContentExpanded}
|
||||
onClose={workspace.closeView}
|
||||
>
|
||||
<div className="catalog-panel-content">{renderSectionContent()}</div>
|
||||
</ApplicationPanel>
|
||||
|
||||
@@ -28,26 +28,6 @@ textarea {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.catalog-header-nav {
|
||||
display: inline-flex;
|
||||
min-height: var(--nodedc-header-pill-height);
|
||||
align-items: center;
|
||||
border-radius: var(--nodedc-radius-circle);
|
||||
background: var(--nodedc-glass-panel-bg-soft);
|
||||
padding: 0.32rem;
|
||||
box-shadow: var(--nodedc-glass-control-shadow);
|
||||
}
|
||||
|
||||
.catalog-header-nav .nodedc-header__nav-item {
|
||||
min-width: 8.5rem;
|
||||
}
|
||||
|
||||
.catalog-header-workspace img {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
[data-nodedc-theme="light"] .catalog-app .nodedc-header__brand img,
|
||||
[data-nodedc-theme="light"] .catalog-app .nodedc-header__workspace img {
|
||||
filter: brightness(0.12);
|
||||
@@ -413,11 +393,11 @@ textarea {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.catalog-header-nav {
|
||||
.catalog-app .nodedc-header-navigation {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.catalog-header-nav .nodedc-header__nav-item {
|
||||
.catalog-app .nodedc-header-navigation .nodedc-header__nav-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user