From 9553c817529b5ba91eb09648bc46ddcf32e3dc00 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Mon, 20 Apr 2026 13:30:04 +0300 Subject: [PATCH] =?UTF-8?q?UI=20-=20=D0=9C=D0=95=D0=96=D0=9F=D0=A0=D0=9E?= =?UTF-8?q?=D0=95=D0=9A=D0=A2=D0=9D=D0=90=D0=AF=20=D0=9A=D0=9E=D0=9C=D0=9C?= =?UTF-8?q?=D0=A3=D0=9D=D0=98=D0=9A=D0=90=D0=A6=D0=98=D0=AF:=20=D0=B2?= =?UTF-8?q?=D0=BD=D0=B5=D1=88=D0=BD=D0=B8=D0=B9=20=D0=BA=D0=BE=D0=BD=D1=82?= =?UTF-8?q?=D1=83=D1=80,=20auth=20empty=20state=20=D0=B8=20HDESIGN=20updat?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HDESIGN-CODE.md | 108 +++++++- .../external-contours/empty-state.tsx | 4 +- .../projects/external-contours/header.tsx | 2 +- .../external-contours/issue-header.tsx | 25 +- .../external-contours/issue-properties.tsx | 16 +- .../projects/external-contours/issue-root.tsx | 6 +- .../projects/external-contours/list-item.tsx | 77 +++--- .../projects/external-contours/root.tsx | 25 +- .../projects/external-contours/sidebar.tsx | 44 +-- .../core/components/auth-screens/header.tsx | 4 +- .../core/components/dropdowns/priority.tsx | 108 ++++---- .../issues/attachment/attachment-upload.tsx | 8 +- .../empty-states/project-view.tsx | 7 +- .../core/components/issues/select/base.tsx | 255 +++++++++--------- .../project-external-contours.store.ts | 1 + plane-src/apps/web/styles/globals.css | 157 +++++++++-- 16 files changed, 552 insertions(+), 295 deletions(-) diff --git a/HDESIGN-CODE.md b/HDESIGN-CODE.md index 368a29d..ff9128a 100644 --- a/HDESIGN-CODE.md +++ b/HDESIGN-CODE.md @@ -99,6 +99,42 @@ - жёсткие border-outline - светлый фон, если основной экран тёмный - Search shell внутри popup должен использовать тот же стиль, что и сам popup. +- Filled CTA внутри popup и модалок подчиняются тому же правилу: + - светлый акцентный фон + - тёмный/чёрный текст + - hover только в более светлый тон того же цвета + +### Portal правило +- Если selector/dropdown открывается внутри: + - scroll-контейнера + - detail-pane + - карточки + - properties section + - sidebar + - sticky header + он не должен рендериться inline. +- Такой popup обязан рендериться на верхнем слое через `portal` (`document.body` или эквивалент). +- Inline popup в ограниченном контейнере считается дефектом, потому что даёт: + - клиппинг + - налезание на соседние блоки + - старую “врезанную” верстку + +### Portal anchor snippet +```tsx +{isOpen && + typeof document !== "undefined" && + createPortal( + +
+ ... +
+
, + document.body + )} +``` ### Reusable классы - Accent CTA: @@ -167,9 +203,9 @@ ## Drag and drop - Drag overlay использует акцентный контур. - Delete dropzone: - - без красного технического свечения + - без красного технического свечения и без red-tinted text/fill - текст локализован - - акцентный outline допустим + - акцентный outline обязателен ## Тексты - Пользовательский UI на русском, если экран русифицирован. @@ -183,3 +219,71 @@ - Если блок визуально расходится со стилем системы, не добавлять поверх временную wrapper-заплатку. Нужно либо перевести блок на shared-компонент, либо переверстать локальную структуру под shared-классы. - Для экранов со вкладками/переключателями нельзя оставлять flash старой верстки. Перед refetch нужно очищать stale store-data и показывать loading shell. - Если карточки или списки разных модулей должны быть одинаковыми по канону, нельзя лечить это внешней обёрткой. Нужно менять сам внутренний layout item-компонента. +- Для `Внешних контуров` это значит: + - список карточек правится на уровне `list-item.tsx`, а не через внешний wrapper + - gap между карточками должен совпадать с каноном `Внутреннего контура` + - актуальный gap списка на текущем каноне: `space-y-2` + - toolbar-навигация и inline actions не должны использовать старые квадратные `IconButton` остатки + - свойства `Приоритет / Метки / Статус` не должны рисовать внутренние boxed-chip артефакты + - filled CTA вроде `Добавить запрос` используют `nodedc-external-primary-button` и всегда имеют тёмный текст + - filled CTA используют чёрный/почти-чёрный текст всегда; белый текст на светлом акценте запрещён + - secondary meta-иконки в карточке списка не должны иметь отдельную серую подложку, если по канону это простой inline icon +- popup выбора `Приоритет / Метки` внутри detail view не рендерится inline в property-row; он обязан уходить в `portal` +- секции с dropdown-trigger внутри blur/glass shell обязаны иметь `overflow: visible` и `isolation: isolate`, иначе popup визуально “тонет” внутри блока +- при переключении `Открытые / Закрытые` store обязан очистить stale request list до нового fetch, чтобы пользователь не видел flash старой верстки +- карточка списка `Внешних контуров` правится на уровне `list-item.tsx`, а не внешней обёрткой: + - верхняя и нижняя оси собираются как у карточки `Внутреннего контура` + - gap между карточками совпадает с каноном `Внутреннего контура` +- empty-state иконки без декоративной подложки; если иконка визуально “плывёт”, корректируется сам SVG/media-box + +### Внешние контуры: code anchors +- Header CTA: +```tsx + +``` + +- List spacing: +```tsx +
+ {filteredRequestIds.map((requestId) => ( + + ))} +
+``` + +- Property control: +```tsx +
+ + ... +
+``` + +- Root tab switch without stale flash: +```tsx +void handleCurrentTab(workspaceSlug, projectId, nextTab); +router.push(`...currentTab=${nextTab}`); +``` + +- Store-side tab reset: +```ts +this.requestIds = []; +this.requests = {}; +this.loader = "init-loading"; +this.currentTab = tab; +``` + +- Portal popup с фиксированной стратегией: +```tsx +const { styles, attributes } = usePopper(referenceElement, popperElement, { + strategy: "fixed", + placement: placement ?? "bottom-start", +}); +``` + +- Контейнер секции с trigger: +```tsx +
+ ... +
+``` diff --git a/plane-src/apps/web/ce/components/projects/external-contours/empty-state.tsx b/plane-src/apps/web/ce/components/projects/external-contours/empty-state.tsx index 5419286..a005200 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/empty-state.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/empty-state.tsx @@ -4,7 +4,7 @@ * See the LICENSE file for details. */ -import { Layers3 } from "lucide-react"; +import { Inbox } from "lucide-react"; import { cn } from "@plane/utils"; type Props = { @@ -19,7 +19,7 @@ export const ExternalContoursEmptyState = (props: Props) => { return (
- +

{title}

diff --git a/plane-src/apps/web/ce/components/projects/external-contours/header.tsx b/plane-src/apps/web/ce/components/projects/external-contours/header.tsx index cf49ca1..bd3ee61 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/header.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/header.tsx @@ -73,7 +73,7 @@ export const ProjectExternalContoursHeader = observer(function ProjectExternalCo variant="primary" size="lg" onClick={() => setCreateIssueModal(true)} - className="nodedc-external-primary-button min-w-[10.75rem]" + className="nodedc-external-primary-button min-w-[13rem]" > {t("external_contours_page.header.add_request")} diff --git a/plane-src/apps/web/ce/components/projects/external-contours/issue-header.tsx b/plane-src/apps/web/ce/components/projects/external-contours/issue-header.tsx index 5868ee0..322f484 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/issue-header.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/issue-header.tsx @@ -9,7 +9,6 @@ import { observer } from "mobx-react"; import { PanelLeft } from "lucide-react"; import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/propel/button"; -import { IconButton } from "@plane/propel/icon-button"; import { CheckCircleFilledIcon, ChevronDownIcon, ChevronUpIcon, CloseCircleFilledIcon, LinkIcon, NewTabIcon } from "@plane/propel/icons"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { TExternalContourRequest, TNameDescriptionLoader } from "@plane/types"; @@ -147,24 +146,24 @@ export const ExternalContoursIssueActionsHeader = observer(function ExternalCont
-
-
- +
+ +
diff --git a/plane-src/apps/web/ce/components/projects/external-contours/issue-properties.tsx b/plane-src/apps/web/ce/components/projects/external-contours/issue-properties.tsx index 0e904de..2cfa98c 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/issue-properties.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/issue-properties.tsx @@ -8,7 +8,7 @@ import { observer } from "mobx-react"; import { SignalHigh } from "lucide-react"; import { ISSUE_PRIORITIES } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { LabelPropertyIcon, PriorityPropertyIcon } from "@plane/propel/icons"; +import { LabelPropertyIcon, PriorityIcon, PriorityPropertyIcon } from "@plane/propel/icons"; import type { TIssue } from "@plane/types"; import { PriorityDropdown } from "@/components/dropdowns/priority"; import { IssueLabelSelect } from "@/components/issues/select"; @@ -34,7 +34,7 @@ export const ExternalContoursIssueContentProperties = observer(function External return (
-
+
{t("external_contours_page.properties.section_title")}
@@ -48,12 +48,12 @@ export const ExternalContoursIssueContentProperties = observer(function External onChange={(val) => issue?.id && issueOperations.update(workspaceSlug, targetProjectId, issue.id, { priority: val })} disabled={!isEditable} buttonVariant="transparent-without-text" - className="flex-1" - buttonContainerClassName="h-full w-full" + className="flex-1 overflow-visible" + buttonContainerClassName="nodedc-external-property-control-shell h-full w-full overflow-visible rounded-[1.25rem] border-0 bg-transparent shadow-none outline-none" button={
{issue.priority && issue.priority !== "none" ? ( - + ) : ( )} @@ -72,14 +72,14 @@ export const ExternalContoursIssueContentProperties = observer(function External {t("labels")}
-
+
issue?.id && issueOperations.update(workspaceSlug, targetProjectId, issue.id, { label_ids: labelIds })} projectId={targetProjectId} disabled={!isEditable} - rootClassName="w-full" - buttonContainerClassName="h-full w-full" + rootClassName="w-full overflow-visible" + buttonContainerClassName="nodedc-external-property-control-shell h-full w-full overflow-visible rounded-[1.25rem] border-0 bg-transparent shadow-none outline-none" label={
diff --git a/plane-src/apps/web/ce/components/projects/external-contours/issue-root.tsx b/plane-src/apps/web/ce/components/projects/external-contours/issue-root.tsx index b3e1ab0..7b70baa 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/issue-root.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/issue-root.tsx @@ -332,11 +332,11 @@ export const ExternalContoursIssueMainContent = observer(function ExternalContou -
+
-
+
-
+
diff --git a/plane-src/apps/web/ce/components/projects/external-contours/list-item.tsx b/plane-src/apps/web/ce/components/projects/external-contours/list-item.tsx index d612a9d..63034c0 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/list-item.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/list-item.tsx @@ -58,39 +58,43 @@ export const ExternalContoursListItem = observer(function ExternalContoursListIt
-
-
- -
-
{requesterName}
-
{contourName}
-
-
-
-
- {issue.project_detail?.identifier || "REQ"}-{issue.sequence_id} -
-
- {request.has_unread_updates && ( - - - - )} - +
+
+
+ +
+
{requesterName}
+
+
+ +
+
+ {issue.project_detail?.identifier || "REQ"}-{issue.sequence_id} +
+
+ {request.has_unread_updates && ( + + + + )} + +
+ +
{contourName}
-
+

{issue.name}

@@ -99,14 +103,15 @@ export const ExternalContoursListItem = observer(function ExternalContoursListIt
{assigneeDetails.length > 0 ? ( - assigneeDetails.map((assignee) => ( - + assigneeDetails.map((assignee, index) => ( +
0 && "-ml-2")}> + +
)) ) : ( @@ -127,7 +132,7 @@ export const ExternalContoursListItem = observer(function ExternalContoursListIt {issue.priority && issue.priority !== "none" && ( -
+
diff --git a/plane-src/apps/web/ce/components/projects/external-contours/root.tsx b/plane-src/apps/web/ce/components/projects/external-contours/root.tsx index 8fd0fb5..af4badf 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/root.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/root.tsx @@ -28,20 +28,31 @@ export const ExternalContoursRoot = observer(function ExternalContoursRoot(props const { workspaceSlug, projectId, inboxIssueId, navigationTab } = props; const [isMobileSidebar, setIsMobileSidebar] = useState(true); const { t } = useTranslation(); - const { loader, error, currentTab, currentProjectId, handleCurrentTab, fetchRequests } = useProjectExternalContours(); + const { loader, error, currentTab, currentProjectId, requestIds, handleCurrentTab, fetchRequests } = + useProjectExternalContours(); useEffect(() => { if (!workspaceSlug || !projectId) return; + const resolvedTab = navigationTab || EInboxIssueCurrentTab.OPEN; const hasProjectChanged = currentProjectId && currentProjectId !== projectId; - if (navigationTab && navigationTab !== currentTab) { - handleCurrentTab(workspaceSlug, projectId, navigationTab); - } else if (hasProjectChanged) { - handleCurrentTab(workspaceSlug, projectId, EInboxIssueCurrentTab.OPEN); - } else { - fetchRequests(workspaceSlug.toString(), projectId.toString(), navigationTab || EInboxIssueCurrentTab.OPEN); + if (hasProjectChanged) { + void handleCurrentTab(workspaceSlug, projectId, EInboxIssueCurrentTab.OPEN); + return; } + + if (currentProjectId === projectId && currentTab === resolvedTab) { + if (loader === "init-loading") return; + if (requestIds.length > 0) return; + } + + if (currentTab !== resolvedTab) { + void handleCurrentTab(workspaceSlug, projectId, resolvedTab); + return; + } + + void fetchRequests(workspaceSlug.toString(), projectId.toString(), resolvedTab); // eslint-disable-next-line react-hooks/exhaustive-deps }, [workspaceSlug, projectId, navigationTab]); diff --git a/plane-src/apps/web/ce/components/projects/external-contours/sidebar.tsx b/plane-src/apps/web/ce/components/projects/external-contours/sidebar.tsx index 2d2c2e3..a4fc116 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/sidebar.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/sidebar.tsx @@ -10,6 +10,7 @@ import { useTranslation } from "@plane/i18n"; import type { TInboxIssueCurrentTab } from "@plane/types"; import { EInboxIssueCurrentTab } from "@plane/types"; import { cn } from "@plane/utils"; +import { useSearchParams } from "next/navigation"; import { useProjectExternalContours } from "@/hooks/store/use-project-external-contours"; import { useAppRouter } from "@/hooks/use-app-router"; import { ExternalContoursEmptyState } from "./empty-state"; @@ -30,17 +31,20 @@ const tabNavigationOptions: { key: TInboxIssueCurrentTab; i18n_label: string }[] export const ExternalContoursSidebar = observer(function ExternalContoursSidebar(props: Props) { const { workspaceSlug, projectId, inboxIssueId, setIsMobileSidebar } = props; const router = useAppRouter(); + const searchParams = useSearchParams(); const { t } = useTranslation(); - const { currentTab, handleCurrentTab, filteredRequestIds, openRequestIds, closedRequestIds, loader } = + const { currentTab, filteredRequestIds, openRequestIds, closedRequestIds, loader, handleCurrentTab } = useProjectExternalContours(); + const routeTab = (searchParams.get("currentTab") as TInboxIssueCurrentTab | null) ?? currentTab; + const isTabTransitioning = loader === "init-loading" || routeTab !== currentTab; useEffect(() => { if (workspaceSlug && projectId && filteredRequestIds.length > 0 && inboxIssueId === undefined) { router.push( - `/${workspaceSlug}/projects/${projectId}/external-contours?currentTab=${currentTab}&inboxIssueId=${filteredRequestIds[0]}` + `/${workspaceSlug}/projects/${projectId}/external-contours?currentTab=${routeTab}&inboxIssueId=${filteredRequestIds[0]}` ); } - }, [currentTab, filteredRequestIds, inboxIssueId, projectId, router, workspaceSlug]); + }, [filteredRequestIds, inboxIssueId, projectId, routeTab, router, workspaceSlug]); return (
@@ -52,27 +56,27 @@ export const ExternalContoursSidebar = observer(function ExternalContoursSidebar return (
); })} @@ -80,14 +84,14 @@ export const ExternalContoursSidebar = observer(function ExternalContoursSidebar
- {loader === "init-loading" ? ( + {isTabTransitioning ? (
{t("loading")}...
) : filteredRequestIds.length > 0 ? ( -
+
{filteredRequestIds.map((requestId) => ( diff --git a/plane-src/apps/web/core/components/dropdowns/priority.tsx b/plane-src/apps/web/core/components/dropdowns/priority.tsx index 89e54d3..917d1ee 100644 --- a/plane-src/apps/web/core/components/dropdowns/priority.tsx +++ b/plane-src/apps/web/core/components/dropdowns/priority.tsx @@ -6,6 +6,7 @@ import type { ReactNode } from "react"; import { useRef, useState } from "react"; +import { createPortal } from "react-dom"; import { usePopper } from "react-popper"; import { SignalHigh } from "lucide-react"; import { Combobox } from "@headlessui/react"; @@ -328,6 +329,7 @@ export function PriorityDropdown(props: Props) { const [popperElement, setPopperElement] = useState(null); // popper-js init const { styles, attributes } = usePopper(referenceElement, popperElement, { + strategy: "fixed", placement: placement ?? "bottom-start", modifiers: [ { @@ -378,7 +380,7 @@ export function PriorityDropdown(props: Props) { - {isDropdownOpen && ( - -
-
- - setQuery(event.target.value)} - placeholder={t("search")} - displayValue={(assigned: any) => assigned?.name} - onKeyDown={searchInputKeyDown} - /> -
-
- {labelsList && filteredOptions ? ( - filteredOptions.length > 0 ? ( - filteredOptions.map((label) => { - const children = labelsList?.filter((l) => l.parent === label.id); + {isDropdownOpen && + typeof document !== "undefined" && + createPortal( + +
+
+ + setQuery(event.target.value)} + placeholder={t("search")} + displayValue={(assigned: any) => assigned?.name} + onKeyDown={searchInputKeyDown} + /> +
+
+ {labelsList && filteredOptions ? ( + filteredOptions.length > 0 ? ( + filteredOptions.map((label) => { + const children = labelsList?.filter((l) => l.parent === label.id); - if (children.length === 0) { - if (!label.parent) - return ( - - cn( - "nodedc-dropdown-option cursor-pointer", - active ? "bg-white/6" : "", - "text-secondary" - ) - } - value={label.id} - > - {({ selected }) => ( -
-
- - {label.name} -
-
- -
-
- )} -
- ); - } else - return ( -
-
- {label.name} -
-
- {children.map((child) => ( - - cn( - "nodedc-dropdown-option min-w-[14rem] cursor-pointer", - active ? "bg-white/6" : "", - "text-secondary" - ) - } - value={child.id} - > - {({ selected }) => ( -
-
- - {child.name} -
-
- -
+ if (children.length === 0) { + if (!label.parent) + return ( + + cn( + "nodedc-dropdown-option cursor-pointer", + active ? "bg-white/6" : "", + "text-secondary" + ) + } + value={label.id} + > + {({ selected }) => ( +
+
+ + {label.name}
- )} - - ))} +
+ +
+
+ )} +
+ ); + } else + return ( +
+
+ {label.name} +
+
+ {children.map((child) => ( + + cn( + "nodedc-dropdown-option min-w-[14rem] cursor-pointer", + active ? "bg-white/6" : "", + "text-secondary" + ) + } + value={child.id} + > + {({ selected }) => ( +
+
+ + {child.name} +
+
+ +
+
+ )} +
+ ))} +
-
- ); - }) - ) : submitting ? ( - - ) : createLabelEnabled ? ( -

{ - if (!query.length) return; - handleAddLabel(query); - }} - className={`rounded-[0.9rem] px-2 py-2 text-left text-secondary ${query.length ? "cursor-pointer hover:bg-white/6" : "cursor-default"}`} - > - {query.length ? ( - <> - + {t("label.create.type")} "{query}" - - ) : ( - t("label.create.type") - )} -

+ ); + }) + ) : submitting ? ( + + ) : createLabelEnabled ? ( +

{ + if (!query.length) return; + handleAddLabel(query); + }} + className={`rounded-[0.9rem] px-2 py-2 text-left text-secondary ${query.length ? "cursor-pointer hover:bg-white/6" : "cursor-default"}`} + > + {query.length ? ( + <> + + {t("label.create.type")} "{query}" + + ) : ( + t("label.create.type") + )} +

+ ) : ( +

{t("no_matching_results")}

+ ) ) : ( -

{t("no_matching_results")}

- ) - ) : ( -

{t("loading")}

- )} +

{t("loading")}

+ )} +
-
- - )} + , + document.body + )} ); }); diff --git a/plane-src/apps/web/core/store/external-contours/project-external-contours.store.ts b/plane-src/apps/web/core/store/external-contours/project-external-contours.store.ts index 822139f..f56e724 100644 --- a/plane-src/apps/web/core/store/external-contours/project-external-contours.store.ts +++ b/plane-src/apps/web/core/store/external-contours/project-external-contours.store.ts @@ -178,6 +178,7 @@ export class ProjectExternalContoursStore implements IProjectExternalContoursSto }; handleCurrentTab = async (workspaceSlug: string, projectId: string, tab: TInboxIssueCurrentTab) => { + this.currentProjectId = projectId; this.requestIds = []; this.requests = {}; this.error = undefined; diff --git a/plane-src/apps/web/styles/globals.css b/plane-src/apps/web/styles/globals.css index 1db6f26..9a77d51 100644 --- a/plane-src/apps/web/styles/globals.css +++ b/plane-src/apps/web/styles/globals.css @@ -980,14 +980,14 @@ } .nodedc-external-tab { - min-height: 2.8rem; + min-height: 3rem; border: 0 !important; outline: none !important; box-shadow: none !important; border-radius: 999px !important; background: transparent !important; color: rgba(255, 255, 255, 0.78) !important; - padding-inline: 1rem !important; + padding-inline: 1.3rem !important; } .nodedc-external-tab:hover { @@ -1025,6 +1025,8 @@ .nodedc-external-content-shell { border: 0 !important; outline: none !important; + overflow: visible !important; + isolation: isolate; box-shadow: 0 18px 44px rgba(0, 0, 0, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.02) !important; @@ -1061,6 +1063,8 @@ .nodedc-external-panel { border: 0 !important; outline: none !important; + overflow: visible !important; + isolation: isolate; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.018) !important; border-radius: 1.6rem !important; background: @@ -1073,6 +1077,8 @@ .nodedc-external-section { border: 0 !important; outline: none !important; + overflow: visible !important; + isolation: isolate; box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.018) !important; @@ -1094,14 +1100,14 @@ } .nodedc-external-action-button { - min-height: 2.75rem; + min-height: 2.85rem; border: 0 !important; outline: none !important; box-shadow: none !important; - border-radius: 1.25rem !important; + border-radius: 1.35rem !important; background: rgba(255, 255, 255, 0.06) !important; color: var(--text-color-primary) !important; - padding-inline: 1.15rem !important; + padding-inline: 1.25rem !important; } .nodedc-external-action-button:hover { @@ -1128,17 +1134,23 @@ } .nodedc-external-primary-button { - min-height: 2.75rem; + min-height: 2.85rem; + min-width: 13rem; border: 0 !important; outline: none !important; box-shadow: none !important; - border-radius: 1.25rem !important; + border-radius: 1.35rem !important; background: rgb(var(--nodedc-card-active-rgb)) !important; color: #0b1117 !important; - padding-inline: 1.45rem !important; + padding-inline: 1.6rem !important; font-weight: 600 !important; } + .nodedc-external-primary-button, + .nodedc-external-primary-button * { + color: #0b1117 !important; + } + .nodedc-external-primary-button:hover { background: color-mix(in srgb, rgb(var(--nodedc-card-active-rgb)) 82%, white) !important; color: #0b1117 !important; @@ -1159,16 +1171,14 @@ place-items: center; width: 6.25rem; height: 6.25rem; - border-radius: 1.85rem; - background: - linear-gradient(180deg, rgba(255, 255, 255, 0.032) 0%, rgba(255, 255, 255, 0.014) 100%), - rgba(255, 255, 255, 0.028); - box-shadow: - 0 16px 40px rgba(0, 0, 0, 0.14), - inset 0 1px 0 rgba(255, 255, 255, 0.018); + border-radius: 0; + background: transparent !important; + box-shadow: none !important; + padding: 0 !important; } .nodedc-external-empty-media svg { + display: block; color: rgba(255, 255, 255, 0.22); } @@ -1189,10 +1199,20 @@ padding: 0.65rem 0.95rem !important; } + .nodedc-external-readonly-value.nodedc-external-readonly-plain { + min-height: 0; + border-radius: 0 !important; + background: transparent !important; + padding: 0 !important; + -webkit-backdrop-filter: none !important; + backdrop-filter: none !important; + } + .nodedc-external-property-row { display: flex; align-items: center; gap: 1rem; + position: relative; border: 0 !important; outline: none !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.018) !important; @@ -1217,37 +1237,134 @@ .nodedc-external-property-value { display: flex; - min-height: 2.5rem; + min-height: 0; width: 100%; align-items: center; + justify-content: flex-start; gap: 0.6rem; + position: relative; + z-index: 1; border: 0 !important; outline: none !important; box-shadow: none !important; border-radius: 999px !important; background: transparent !important; color: var(--text-color-primary) !important; - padding: 0.1rem 0.1rem !important; + padding: 0 !important; } .nodedc-external-property-control { display: flex; - min-height: 2.5rem; + min-height: 0; width: 100%; align-items: center; + justify-content: flex-start; gap: 0.6rem; + position: relative; + z-index: 1; border: 0 !important; outline: none !important; box-shadow: none !important; border-radius: 999px !important; background: transparent !important; color: var(--text-color-primary) !important; - padding: 0.1rem 0.1rem !important; + padding: 0 !important; transition: background 160ms ease; } + .nodedc-external-property-control-shell { + border: 0 !important; + outline: none !important; + box-shadow: none !important; + background: transparent !important; + padding: 0 !important; + } + + .nodedc-external-property-control-shell:hover, + .nodedc-external-property-control-shell:focus, + .nodedc-external-property-control-shell:focus-visible, + .nodedc-external-property-control-shell:focus-within { + border: 0 !important; + outline: none !important; + box-shadow: none !important; + background: transparent !important; + } + .nodedc-external-property-control:hover, .nodedc-external-property-control:focus-within { - background: rgba(255, 255, 255, 0.04) !important; + background: transparent !important; + } + + .nodedc-external-popup-anchor { + z-index: 760 !important; + } + + .nodedc-external-detail-toolbar { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + } + + .nodedc-external-toolbar-cluster { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0; + border-radius: 999px; + background: transparent !important; + } + + .nodedc-external-priority-inline { + display: inline-flex; + align-items: center; + gap: 0.375rem; + color: var(--text-color-secondary) !important; + background: transparent !important; + box-shadow: none !important; + border: 0 !important; + outline: none !important; + } + + .nodedc-attachment-upload { + min-height: 4.5rem; + border: 0 !important; + outline: none !important; + box-shadow: + inset 0 0 0 1px rgba(var(--nodedc-accent-rgb), 0.18), + inset 0 1px 0 rgba(255, 255, 255, 0.018), + 0 10px 28px rgba(0, 0, 0, 0.08) !important; + border-radius: 1.35rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%), + rgba(255, 255, 255, 0.025) !important; + color: var(--text-color-secondary) !important; + transition: + background 180ms ease, + color 180ms ease, + box-shadow 180ms ease; + } + + .nodedc-attachment-upload:hover, + .nodedc-attachment-upload[data-drag-active="true"] { + box-shadow: + inset 0 0 0 1px rgba(var(--nodedc-accent-rgb), 0.32), + inset 0 1px 0 rgba(255, 255, 255, 0.018), + 0 10px 28px rgba(0, 0, 0, 0.08) !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.038) 0%, rgba(255, 255, 255, 0.014) 100%), + rgba(255, 255, 255, 0.035) !important; + color: var(--text-color-primary) !important; + } + + .nodedc-attachment-upload[data-drag-reject="true"] { + box-shadow: + inset 0 0 0 1px rgba(var(--nodedc-accent-rgb), 0.4), + inset 0 1px 0 rgba(255, 255, 255, 0.018), + 0 10px 28px rgba(0, 0, 0, 0.08) !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.012) 100%), + rgba(255, 255, 255, 0.03) !important; + color: var(--text-color-primary) !important; } }