@exxatdesignux/ui 0.2.16 → 0.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +148 -3
- package/consumer-extras/cursor-skills/exxat-ds-skill/references/accessibility.md +142 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/references/coach-marks.md +169 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +382 -0
- package/consumer-extras/cursor-skills/exxat-mono-ids/SKILL.md +56 -0
- package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +14 -0
- package/package.json +3 -3
- package/src/components/ui/banner.tsx +2 -0
- package/src/components/ui/chart.tsx +57 -2
- package/src/components/ui/sidebar.tsx +1 -0
- package/template/.claude/skills/exxat-ds-skill/SKILL.md +1 -1
- package/template/.cursor/rules/exxat-mono-ids.mdc +30 -0
- package/template/AGENTS.md +18 -15
- package/template/app/(app)/data-list/page.tsx +2 -2
- package/template/app/(app)/question-bank/layout.tsx +18 -5
- package/template/app/(app)/question-bank/new/page.tsx +58 -0
- package/template/app/globals.css +108 -1
- package/template/app/layout.tsx +41 -5
- package/template/components/app-sidebar.tsx +68 -34
- package/template/components/ask-leo-sidebar.tsx +0 -2
- package/template/components/brand-color-picker.tsx +344 -0
- package/template/components/compliance-list-view.tsx +33 -51
- package/template/components/compliance-table.tsx +24 -0
- package/template/components/data-table/index.tsx +68 -24
- package/template/components/data-table/pagination.tsx +0 -1
- package/template/components/data-table/types.ts +4 -1
- package/template/components/data-table/use-table-state.ts +243 -94
- package/template/components/data-views/data-row-list.tsx +183 -0
- package/template/components/data-views/index.ts +7 -3
- package/template/components/data-views/os-folder-glyph.tsx +8 -0
- package/template/components/export-drawer.tsx +1 -1
- package/template/components/exxat-product-logo.tsx +172 -317
- package/template/components/invite-collaborators-drawer.tsx +5 -3
- package/template/components/key-metrics.tsx +74 -46
- package/template/components/new-placement-form.tsx +4 -2
- package/template/components/new-question-composer.tsx +2208 -0
- package/template/components/page-breadcrumb-trail.tsx +131 -0
- package/template/components/page-header.tsx +2 -1
- package/template/components/{data-views/placement-board-card.tsx → placement-board-card.tsx} +1 -1
- package/template/components/placement-detail.tsx +1 -1
- package/template/components/placements-board-view.tsx +1 -1
- package/template/components/{data-list-client.tsx → placements-client.tsx} +9 -7
- package/template/components/placements-list-view.tsx +18 -132
- package/template/components/{data-list-table-cells.test.tsx → placements-table-cells.test.tsx} +2 -2
- package/template/components/{data-list-table-cells.tsx → placements-table-cells.tsx} +1 -1
- package/template/components/placements-table-columns.tsx +2 -2
- package/template/components/{data-list-table.tsx → placements-table.tsx} +67 -58
- package/template/components/product-switcher.tsx +26 -8
- package/template/components/product-wordmark.tsx +285 -0
- package/template/components/question-bank-client.tsx +20 -2
- package/template/components/question-bank-hub-client.tsx +108 -115
- package/template/components/question-bank-list-view.tsx +30 -54
- package/template/components/question-bank-new-folder-sheet.tsx +1 -1
- package/template/components/question-bank-secondary-nav.tsx +0 -3
- package/template/components/question-bank-table.tsx +30 -5
- package/template/components/rotations-empty-state.tsx +3 -0
- package/template/components/secondary-panel.tsx +23 -3
- package/template/components/settings-appearance-card.tsx +584 -141
- package/template/components/site-header.tsx +36 -31
- package/template/components/sites-list-view.tsx +31 -36
- package/template/components/sites-table.tsx +24 -0
- package/template/components/table-properties/drawer.tsx +1 -1
- package/template/components/team-client.tsx +1 -1
- package/template/components/team-list-view.tsx +34 -50
- package/template/components/team-table.tsx +29 -3
- package/template/components/templates/nested-secondary-panel-shell.tsx +8 -2
- package/template/components/ui/dot-pattern.tsx +50 -26
- package/template/components/ui/leo-icon.tsx +23 -3
- package/template/contexts/product-context.tsx +51 -7
- package/template/contexts/system-banner-context.tsx +112 -4
- package/template/eslint.config.mjs +18 -0
- package/template/hooks/use-sidebar-reflow-zoom.ts +21 -11
- package/template/lib/data-list-persistence.ts +57 -257
- package/template/lib/dev-log.test.ts +6 -5
- package/template/lib/exxat-palette.json +1462 -0
- package/template/lib/exxat-palette.ts +136 -0
- package/template/lib/list-page-table-properties.ts +1 -1
- package/template/lib/list-status-badges.ts +1 -1
- package/template/lib/mailto.ts +29 -0
- package/template/lib/placement-board-card-layout.ts +1 -1
- package/template/lib/product-brand.ts +268 -0
- package/template/lib/question-bank-authoring.ts +308 -0
- package/template/lib/question-bank-nav.ts +44 -0
- package/template/lib/raf-throttle.ts +45 -0
- package/template/lib/table-state-lifecycle.ts +474 -0
- package/template/next.config.mjs +156 -0
- package/template/package.json +3 -3
- package/template/stores/app-store.ts +46 -1
|
@@ -1,34 +1,56 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* soft grey (#A8B2BA) on dark/HC; product name letters (One/Prism) use
|
|
6
|
-
* brand pink (#E31B79) by default.
|
|
4
|
+
* Exxat product logo — SVG (mark + "Exxat" letters) + HTML text (product suffix).
|
|
7
5
|
*
|
|
8
|
-
*
|
|
6
|
+
* Why the hybrid:
|
|
7
|
+
* - The mark and the "Exxat" prefix are **static** and shared across every
|
|
8
|
+
* Exxat product. Tracing them once as SVG paths means pixel-perfect
|
|
9
|
+
* rendering, no font-loading flicker, and perfect baseline alignment
|
|
10
|
+
* between the circular mark and the "Exxat" caps (they live in the same
|
|
11
|
+
* `viewBox`).
|
|
12
|
+
* - The **suffix** (One / Prism / Pulse / …) is the only variable part, so it
|
|
13
|
+
* renders as HTML text in **IvyPresto Text SemiBold** per the official
|
|
14
|
+
* Figma brand spec (`weight 600`, `letter-spacing -3 %`). Adding a new
|
|
15
|
+
* product is still a one-liner in `lib/product-brand.ts` — no new path
|
|
16
|
+
* tracing required.
|
|
9
17
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
18
|
+
* The earlier "all-HTML wordmark" approach worked but couldn't fully match the
|
|
19
|
+
* SVG's letter spacing / weight (Inter ≠ the original traced glyphs) and made
|
|
20
|
+
* the mark-to-Exxat alignment depend on browser font-metrics. The hybrid
|
|
21
|
+
* removes both classes of issue.
|
|
22
|
+
*
|
|
23
|
+
* **Reference paths:** the mark + "Exxat" path data is the same `d=…` source
|
|
24
|
+
* used by the original `ExxatOneLogo` in `packages/ui/template/components/
|
|
25
|
+
* exxat-product-logo.tsx` (the pre-text-route baseline). Only the gradient
|
|
26
|
+
* colours are pulled from the brand registry so new brands recolour the mark.
|
|
13
27
|
*/
|
|
14
28
|
|
|
15
29
|
import * as React from "react"
|
|
16
30
|
import { cn } from "@/lib/utils"
|
|
31
|
+
import { ProductMark } from "@/components/product-wordmark"
|
|
32
|
+
import { brandForProduct, type ProductBrandConfig } from "@/lib/product-brand"
|
|
17
33
|
import type { Product } from "@/contexts/product-context"
|
|
18
|
-
|
|
19
|
-
const WM = "fill-[#273441] dark:fill-[#A8B2BA]"
|
|
20
|
-
|
|
21
|
-
/** Wordmark `<g>` only — `dark:` so light mode keeps brand pink / grey. */
|
|
22
|
-
const MUTED_SUFFIX_CLASS =
|
|
23
|
-
"dark:[&_[data-exxat-product-wordmark]_path]:!fill-[var(--muted-foreground)]"
|
|
34
|
+
import { useAppStore } from "@/stores/app-store"
|
|
24
35
|
|
|
25
36
|
export type ExxatProductLogoVariant = "default" | "mutedSuffix"
|
|
26
37
|
|
|
38
|
+
export interface ExxatProductLogoProps {
|
|
39
|
+
product: Product
|
|
40
|
+
className?: string
|
|
41
|
+
/** Sidebar / switcher: muted wordmark in dark mode only; light keeps brand color. */
|
|
42
|
+
variant?: ExxatProductLogoVariant
|
|
43
|
+
}
|
|
44
|
+
|
|
27
45
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
46
|
+
* The shared viewBox covers x=0..147 (mark), the original 49-unit baseline gap
|
|
47
|
+
* (x=147..196), and x=196..514 (the "Exxat" letters). The full y=0..164 height
|
|
48
|
+
* preserves the breathing room around the circle exactly as the brand asset
|
|
49
|
+
* defines it — DO NOT crop or the mark stops being a round disc.
|
|
31
50
|
*/
|
|
51
|
+
const EXXAT_LOGO_VIEWBOX = "0 0 514 164"
|
|
52
|
+
|
|
53
|
+
/** Defer `<defs>` until after mount so SSR/CSR ids match — see `ProductMark`. */
|
|
32
54
|
function useBrowserPaintReady() {
|
|
33
55
|
const [ready, setReady] = React.useState(false)
|
|
34
56
|
React.useLayoutEffect(() => {
|
|
@@ -37,356 +59,189 @@ function useBrowserPaintReady() {
|
|
|
37
59
|
return ready
|
|
38
60
|
}
|
|
39
61
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function useMarkGradientId(prefix: string) {
|
|
62
|
+
/**
|
|
63
|
+
* Per-instance gradient id so multiple logos on one page don't collide. Strip
|
|
64
|
+
* colons because Radix-style `useId()` values can contain them and SVG `id`
|
|
65
|
+
* cannot.
|
|
66
|
+
*/
|
|
67
|
+
function useExxatBaseGradientId(brandId: string) {
|
|
50
68
|
const raw = React.useId().replace(/:/g, "")
|
|
51
|
-
return
|
|
69
|
+
return `exxat-base-${brandId.replace(/[^a-z0-9-]/gi, "")}-${raw}`
|
|
52
70
|
}
|
|
53
71
|
|
|
54
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Mark + "Exxat" letters as a single SVG. The "Exxat" letter paths are
|
|
74
|
+
* authored at y=35..128 (cap top / baseline) inside the 164-unit viewBox, so
|
|
75
|
+
* the cap mid-line sits ≈ 81.5 — naturally centred with the circular mark
|
|
76
|
+
* whose centre is at ≈ 81.5 too. This shared centring is the whole reason for
|
|
77
|
+
* baking both into the same SVG.
|
|
78
|
+
*/
|
|
79
|
+
function ExxatLogoBase({
|
|
80
|
+
config,
|
|
55
81
|
className,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const sharedClass = cn(
|
|
63
|
-
"block h-full w-auto max-w-none object-left object-contain",
|
|
64
|
-
variant === "mutedSuffix" && MUTED_SUFFIX_CLASS,
|
|
65
|
-
className,
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
if (!ready) {
|
|
69
|
-
return (
|
|
70
|
-
<svg
|
|
71
|
-
viewBox="0 0 766 164"
|
|
72
|
-
fill="none"
|
|
73
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
74
|
-
data-product-logo
|
|
75
|
-
className={sharedClass}
|
|
76
|
-
aria-hidden
|
|
77
|
-
suppressHydrationWarning
|
|
78
|
-
{...props}
|
|
79
|
-
/>
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return (
|
|
84
|
-
<svg
|
|
85
|
-
viewBox="0 0 766 164"
|
|
86
|
-
fill="none"
|
|
87
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
88
|
-
data-product-logo
|
|
89
|
-
className={sharedClass}
|
|
90
|
-
aria-hidden
|
|
91
|
-
suppressHydrationWarning
|
|
92
|
-
{...props}
|
|
93
|
-
>
|
|
94
|
-
<g clipPath={`url(#${clip0})`}>
|
|
95
|
-
<path
|
|
96
|
-
d="M73.4939 155.238C114.084 155.238 146.988 122.334 146.988 81.7439C146.988 41.1544 114.084 8.25 73.4939 8.25C32.9044 8.25 0 41.1544 0 81.7439C0 122.334 32.9044 155.238 73.4939 155.238Z"
|
|
97
|
-
fill={`url(#${paint0})`}
|
|
98
|
-
/>
|
|
99
|
-
<path
|
|
100
|
-
d="M0.594727 90.9915C4.59951 122.921 29.0894 148.466 60.4966 154.085L102.462 116.355V102.302H86.8312L102.462 88.2489V74.1957H86.8312L102.462 60.1425V46.0894H50.5575L0.594727 90.9915Z"
|
|
101
|
-
fill="#BE1E6D"
|
|
102
|
-
/>
|
|
103
|
-
<path d="M102.474 116.355H50.5576L58.6764 102.302H102.474V116.355Z" fill="white" />
|
|
104
|
-
<path d="M102.474 60.1303H58.6764L50.5576 46.0771H102.474V60.1303Z" fill="white" />
|
|
105
|
-
<path d="M102.474 88.2368H66.7949L70.8483 81.2102L66.7949 74.1836H102.474V88.2368Z" fill="white" />
|
|
106
|
-
<path d="M39.2227 74.1835H66.795L58.6762 60.1304H39.2227V74.1835Z" fill="white" />
|
|
107
|
-
<path d="M39.2227 102.302H58.6762L66.795 88.2368H39.2227V102.302Z" fill="white" />
|
|
108
|
-
</g>
|
|
109
|
-
<g clipPath={`url(#${clip1})`} data-exxat-product-wordmark>
|
|
110
|
-
<path
|
|
111
|
-
d="M579.64 32.6512C592.027 32.3136 602.339 34.2511 611.886 42.8549C621.616 51.9225 625.419 64.8345 626.05 77.6623C627.31 103.322 613.505 127.896 585.51 129.148C585.162 129.199 584.811 129.234 584.461 129.252C574.085 129.753 562.004 126.318 554.126 119.188C536.921 103.616 535.647 72.9436 546.054 53.3303C552.613 40.9652 565.665 33.3388 579.64 32.6512ZM584.116 123.144C589.474 122.455 593.908 120.383 597.184 115.86C606.219 102.471 605.632 82.6132 604.178 67.1363C603.351 58.3677 600.264 47.6909 593.184 42.0268C590.322 39.7365 585.27 38.2385 581.604 38.7657C572.591 39.5696 567.54 44.6598 564.488 52.8846C558.062 70.2058 555.754 124.532 584.116 123.144Z"
|
|
112
|
-
fill="#E31B79"
|
|
113
|
-
/>
|
|
114
|
-
<path
|
|
115
|
-
d="M430.755 55.7344C443.603 55.2553 459.709 58.3988 463.175 73.143C464.166 77.3587 463.884 82.6952 463.877 87.0408L463.845 105.91C463.857 112.149 463.051 112.645 469.328 113.205C469.057 117.66 469.234 123.628 469.242 128.194C461.169 128.149 448.96 129.818 446.764 119.668C444.471 122.422 443.567 123.605 440.357 125.604C433.884 129.636 423.423 129.934 416.178 128.172C410.382 126.762 405.62 123.519 402.512 118.29C400.527 114.233 400.122 109.477 400.649 105.072C402.505 89.5606 418.762 87.5983 431.172 85.928C435.522 85.237 440.828 84.6483 444.472 82.0073C447.548 79.7708 447.168 76.525 444.97 73.7922C440.68 68.4582 429.516 68.0981 424.356 72.2079C421.359 74.5939 420.834 77.8667 420.5 81.4362C414.437 81.3803 408.373 81.3842 402.31 81.4478C402.503 79.5217 402.653 77.3957 403.031 75.5059C405.774 61.7822 418.095 56.4115 430.755 55.7344ZM420.852 112.903C428.033 116.948 440.992 113.872 444.937 106.311C445.852 104.557 447.932 97.6766 446.702 95.9674L446.344 95.9145C442.708 97.4881 435.913 98.8678 431.795 99.3372C425.343 100.072 411.452 104.687 420.852 112.903Z"
|
|
116
|
-
className={WM}
|
|
117
|
-
/>
|
|
118
|
-
<path
|
|
119
|
-
d="M677.935 59.0477C699.855 57.8913 696.583 76.9682 696.748 92.0377C696.845 100.807 696.517 109.637 697.098 118.395C697.259 120.816 697.771 123.787 700.211 124.947C701.104 125.374 701.849 125.466 702.614 126.131C702.778 127.168 702.853 126.832 702.5 127.735C699.251 128.44 678.411 127.93 673.804 127.775C673.206 127.755 672.953 127.462 672.68 127.015C673.231 125.409 675.671 125.143 677.582 123.199C679.398 114.08 678.719 105.254 678.844 95.9909C678.858 90.3513 678.931 84.9044 678.377 79.2797C677.042 65.7365 665.601 69.2473 656.927 73.9796C657.192 76.486 657.033 81.4311 657.025 84.1135L657 106.393C656.994 112.303 656.332 118.583 658.741 124.027C660.446 125.154 662.221 124.835 663.019 126.793C662.85 127.589 663.003 127.49 662.488 127.922C658.98 128.008 635.083 128.474 633.558 127.735C632.96 127.446 632.882 127.238 632.676 126.643C633.394 124.87 634.977 125.133 636.384 124.383C637.23 123.934 637.658 122.7 637.906 121.828C639.341 116.75 639.266 75.7628 637.889 71.6175C636.952 68.8035 633.614 67.9918 632.445 65.6353C632.095 64.9279 632.198 64.6117 632.418 63.913C635.133 62.7812 653.765 59.3261 657.053 59.0324L656.98 68.2382C663.158 62.7473 669.782 59.9066 677.935 59.0477Z"
|
|
120
|
-
fill="#E31B79"
|
|
121
|
-
/>
|
|
122
|
-
<path
|
|
123
|
-
d="M331.798 57.0712C337.592 57.1185 343.354 57.0072 349.156 57.1749C351.068 59.3357 353.387 63.1655 355.063 65.6344C358.847 71.2087 362.432 77.014 366.36 82.4907C370.845 75.0665 378.273 64.1367 383.331 57.1032C385.626 57.0945 399.689 56.8398 400.866 57.3117C401.39 58.6028 399.759 61.1078 399.008 62.1658C392.221 71.7261 386.21 82.4699 379.267 91.8592C383.35 97.6717 387.268 104.526 391.166 110.532C393.16 113.606 400.626 124.783 400.949 127.559C399.879 128.243 384.096 127.95 382.001 127.951C377.115 119.684 371.356 110.895 366.217 102.718C364.993 105.113 362.366 109.016 360.85 111.436C357.401 116.922 353.992 122.433 350.622 127.968C348.242 127.897 332.898 128.279 332.168 127.57C332.118 126.754 332.069 125.826 332.449 125.099C334.495 121.173 337.285 117.058 339.661 113.307L353.193 91.8005C352.418 90.7063 351.628 89.4998 350.898 88.362C344.705 78.7208 337.866 69.3865 332.081 59.5087C331.713 58.8803 331.753 57.7841 331.798 57.0712Z"
|
|
124
|
-
className={WM}
|
|
125
|
-
/>
|
|
126
|
-
<path
|
|
127
|
-
d="M311.843 57.1062C314.843 57.097 327.108 56.8601 329.377 57.2135L329.606 57.8503C329.333 60.3776 324.215 67.3436 322.511 69.9223C317.651 77.1875 312.847 84.4905 308.101 91.8305C309.118 93.8473 311.981 98.1472 313.27 100.2L323.962 117.113C325.904 120.182 329.179 124.459 329.551 127.99C323.658 127.748 316.574 127.94 310.593 127.946C307.782 122.799 304.084 117.694 301.099 112.597C299.196 109.347 296.932 105.769 294.706 102.746C293.772 104.889 290.7 109.57 289.357 111.724C285.993 117.165 282.58 122.576 279.12 127.956C276.595 127.908 261.817 128.243 260.671 127.615C260.249 126.007 261.797 123.527 262.702 122.205C269.332 112.519 275.107 101.261 281.979 91.8322C281.555 91.3258 281.153 90.8019 280.774 90.2617C279.987 89.1263 279.24 87.9296 278.486 86.7642C272.548 77.5965 266.263 68.6263 260.479 59.3668C260.192 58.9075 260.442 57.6507 260.539 57.1068C266.332 57.0478 272.125 57.0656 277.917 57.1596C283.454 64.9372 289.73 74.44 294.84 82.5018C296.784 80.011 299.246 76.0686 301.022 73.3917L311.843 57.1062Z"
|
|
128
|
-
className={WM}
|
|
129
|
-
/>
|
|
130
|
-
<path
|
|
131
|
-
d="M479.835 35.8541C485.68 35.8897 491.521 35.8895 497.366 35.853C497.152 42.4806 497.332 50.2661 497.324 56.9807L514.284 56.9509L514.292 72.0978C508.643 72.0524 502.993 72.036 497.341 72.0485L497.313 93.563C497.313 97.0693 496.581 108.123 499.41 110.474C502.231 112.82 510.459 112.62 514.295 112.135L514.279 123.529L514.292 127.437C511.116 127.895 507.911 128.146 504.704 128.188C479.71 128.491 479.891 117.266 479.916 96.9484C479.949 88.6621 479.938 80.3759 479.88 72.09C476.431 72.026 471.882 71.9331 468.516 72.1906C468.292 67.7063 468.488 61.552 468.523 56.9865C469.391 56.999 470.258 56.9979 471.126 56.9829C482.417 56.741 480.091 43.7789 479.835 35.8541Z"
|
|
132
|
-
className={WM}
|
|
133
|
-
/>
|
|
134
|
-
<path
|
|
135
|
-
d="M196 35.7646L235.626 35.811C239.705 35.8108 250.804 36.0941 254.421 35.6509L254.407 50.8756C240.766 50.8038 227.125 50.8041 213.485 50.877L213.495 74.3467C224.554 74.3448 238.413 74.7338 249.193 74.2652L249.203 89.7182C245.211 89.4232 239.525 89.5845 235.431 89.5872L213.496 89.6342L213.484 113.004L256.078 112.926L256.072 128.097C251.917 127.617 239.134 127.901 234.375 127.929C221.693 128.004 208.639 127.754 196 127.94V35.7646Z"
|
|
136
|
-
className={WM}
|
|
137
|
-
/>
|
|
138
|
-
<path
|
|
139
|
-
d="M765.695 111.581C764.975 112.808 764.201 114.145 763.431 115.334C759.103 122.071 752.257 126.795 744.421 128.451C738.137 129.794 731.661 129.518 725.655 127.155C704.375 118.784 701.479 90.2098 712.245 72.5278C724.1 53.0555 757.792 53.4083 764.26 77.0772C764.933 79.5425 765.164 82.3339 765.695 84.8904V88.317C761.667 88.6393 755.214 88.441 751.038 88.436L724.401 88.4051C725.018 94.4891 725.082 99.3499 727.666 104.929C735.589 122.017 750.591 119.084 762.151 108.735C763.172 109.32 764.655 110.224 765.695 110.669V111.581ZM737.739 82.3573C741.742 82.3648 745.848 82.325 749.842 82.3896C749.575 74.8096 748.304 62.8235 737.466 64.6145C727.219 65.6478 726.515 74.7732 724.812 82.424C729.121 82.3579 733.43 82.3356 737.739 82.3573Z"
|
|
140
|
-
fill="#E31B79"
|
|
141
|
-
/>
|
|
142
|
-
</g>
|
|
143
|
-
<defs>
|
|
144
|
-
<linearGradient
|
|
145
|
-
id={paint0}
|
|
146
|
-
x1="28.3733"
|
|
147
|
-
y1="134.255"
|
|
148
|
-
x2="117.195"
|
|
149
|
-
y2="30.9074"
|
|
150
|
-
gradientUnits="userSpaceOnUse"
|
|
151
|
-
>
|
|
152
|
-
<stop offset="0.04" stopColor="#E21C79" />
|
|
153
|
-
<stop offset="0.65" stopColor="#E21E7B" />
|
|
154
|
-
<stop offset="0.73" stopColor="#E42880" />
|
|
155
|
-
<stop offset="0.88" stopColor="#E9448E" />
|
|
156
|
-
<stop offset="1" stopColor="#EF609D" />
|
|
157
|
-
</linearGradient>
|
|
158
|
-
<clipPath id={clip0}>
|
|
159
|
-
<rect width="147" height="147" fill="white" transform="translate(0 8.25)" />
|
|
160
|
-
</clipPath>
|
|
161
|
-
<clipPath id={clip1}>
|
|
162
|
-
<rect width="569.695" height="163.5" fill="white" transform="translate(196)" />
|
|
163
|
-
</clipPath>
|
|
164
|
-
</defs>
|
|
165
|
-
</svg>
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/** Circular mark only — collapsed sidebar (matches school Avatar 32×32). */
|
|
170
|
-
function ExxatOneMark({ className, ...props }: React.ComponentProps<"svg">) {
|
|
82
|
+
style,
|
|
83
|
+
}: {
|
|
84
|
+
config: ProductBrandConfig
|
|
85
|
+
className?: string
|
|
86
|
+
style?: React.CSSProperties
|
|
87
|
+
}) {
|
|
171
88
|
const ready = useBrowserPaintReady()
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
89
|
+
const gradId = useExxatBaseGradientId(config.id)
|
|
90
|
+
const [from, to] = config.markGradient ?? [config.brandColor, config.brandColor]
|
|
91
|
+
const shadow = config.markShadow ?? config.brandColor
|
|
92
|
+
|
|
93
|
+
const sharedProps = {
|
|
94
|
+
viewBox: EXXAT_LOGO_VIEWBOX,
|
|
95
|
+
preserveAspectRatio: "xMinYMid meet" as const,
|
|
96
|
+
fill: "none",
|
|
97
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
98
|
+
"data-product-logo-base": true,
|
|
99
|
+
"data-product-id": config.id,
|
|
100
|
+
"aria-hidden": true,
|
|
101
|
+
suppressHydrationWarning: true,
|
|
102
|
+
className: cn("block shrink-0 aspect-[514/164]", className),
|
|
103
|
+
style,
|
|
104
|
+
} as const
|
|
177
105
|
|
|
178
106
|
if (!ready) {
|
|
179
|
-
return
|
|
180
|
-
<svg
|
|
181
|
-
viewBox="0 8.25 147 147"
|
|
182
|
-
preserveAspectRatio="xMidYMid meet"
|
|
183
|
-
fill="none"
|
|
184
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
185
|
-
data-product-logo-mark
|
|
186
|
-
className={sharedClass}
|
|
187
|
-
aria-hidden
|
|
188
|
-
suppressHydrationWarning
|
|
189
|
-
{...props}
|
|
190
|
-
/>
|
|
191
|
-
)
|
|
107
|
+
return <svg {...sharedProps} />
|
|
192
108
|
}
|
|
193
109
|
|
|
194
110
|
return (
|
|
195
|
-
<svg
|
|
196
|
-
|
|
197
|
-
preserveAspectRatio="xMidYMid meet"
|
|
198
|
-
fill="none"
|
|
199
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
200
|
-
data-product-logo-mark
|
|
201
|
-
className={sharedClass}
|
|
202
|
-
aria-hidden
|
|
203
|
-
suppressHydrationWarning
|
|
204
|
-
{...props}
|
|
205
|
-
>
|
|
111
|
+
<svg {...sharedProps}>
|
|
112
|
+
{/* ── Mark: outer circle, inner shadow plate, cut-out "E" strokes ── */}
|
|
206
113
|
<path
|
|
207
114
|
d="M73.4939 155.238C114.084 155.238 146.988 122.334 146.988 81.7439C146.988 41.1544 114.084 8.25 73.4939 8.25C32.9044 8.25 0 41.1544 0 81.7439C0 122.334 32.9044 155.238 73.4939 155.238Z"
|
|
208
|
-
fill={`url(#${
|
|
115
|
+
fill={`url(#${gradId})`}
|
|
209
116
|
/>
|
|
210
117
|
<path
|
|
211
118
|
d="M0.594727 90.9915C4.59951 122.921 29.0894 148.466 60.4966 154.085L102.462 116.355V102.302H86.8312L102.462 88.2489V74.1957H86.8312L102.462 60.1425V46.0894H50.5575L0.594727 90.9915Z"
|
|
212
|
-
fill=
|
|
119
|
+
fill={shadow}
|
|
213
120
|
/>
|
|
214
121
|
<path d="M102.474 116.355H50.5576L58.6764 102.302H102.474V116.355Z" fill="white" />
|
|
215
122
|
<path d="M102.474 60.1303H58.6764L50.5576 46.0771H102.474V60.1303Z" fill="white" />
|
|
216
123
|
<path d="M102.474 88.2368H66.7949L70.8483 81.2102L66.7949 74.1836H102.474V88.2368Z" fill="white" />
|
|
217
124
|
<path d="M39.2227 74.1835H66.795L58.6762 60.1304H39.2227V74.1835Z" fill="white" />
|
|
218
125
|
<path d="M39.2227 102.302H58.6762L66.795 88.2368H39.2227V102.302Z" fill="white" />
|
|
219
|
-
<defs>
|
|
220
|
-
<linearGradient
|
|
221
|
-
id={paint0}
|
|
222
|
-
x1="28.3733"
|
|
223
|
-
y1="134.255"
|
|
224
|
-
x2="117.195"
|
|
225
|
-
y2="30.9074"
|
|
226
|
-
gradientUnits="userSpaceOnUse"
|
|
227
|
-
>
|
|
228
|
-
<stop offset="0.04" stopColor="#E21C79" />
|
|
229
|
-
<stop offset="0.65" stopColor="#E21E7B" />
|
|
230
|
-
<stop offset="0.73" stopColor="#E42880" />
|
|
231
|
-
<stop offset="0.88" stopColor="#E9448E" />
|
|
232
|
-
<stop offset="1" stopColor="#EF609D" />
|
|
233
|
-
</linearGradient>
|
|
234
|
-
</defs>
|
|
235
|
-
</svg>
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
export interface ExxatProductMarkProps {
|
|
240
|
-
product: Product
|
|
241
|
-
className?: string
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export function ExxatProductMark({ product: _product, className }: ExxatProductMarkProps) {
|
|
245
|
-
return <ExxatOneMark className={className} />
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function ExxatPrismLogo({
|
|
249
|
-
className,
|
|
250
|
-
variant = "default",
|
|
251
|
-
...props
|
|
252
|
-
}: React.ComponentProps<"svg"> & { variant?: ExxatProductLogoVariant }) {
|
|
253
|
-
const ready = useBrowserPaintReady()
|
|
254
|
-
const { clip0, clip1, paint0 } = useLogoIds("prism")
|
|
255
126
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
className,
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
if (!ready) {
|
|
263
|
-
return (
|
|
264
|
-
<svg
|
|
265
|
-
viewBox="0 0 871 164"
|
|
266
|
-
fill="none"
|
|
267
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
268
|
-
data-product-logo
|
|
269
|
-
className={sharedClass}
|
|
270
|
-
aria-hidden
|
|
271
|
-
suppressHydrationWarning
|
|
272
|
-
{...props}
|
|
273
|
-
/>
|
|
274
|
-
)
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return (
|
|
278
|
-
<svg
|
|
279
|
-
viewBox="0 0 871 164"
|
|
280
|
-
fill="none"
|
|
281
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
282
|
-
data-product-logo
|
|
283
|
-
className={sharedClass}
|
|
284
|
-
aria-hidden
|
|
285
|
-
suppressHydrationWarning
|
|
286
|
-
{...props}
|
|
287
|
-
>
|
|
288
|
-
<g clipPath={`url(#${clip0})`}>
|
|
289
|
-
<path
|
|
290
|
-
d="M73.4939 155.238C114.084 155.238 146.988 122.334 146.988 81.7439C146.988 41.1544 114.084 8.25 73.4939 8.25C32.9044 8.25 0 41.1544 0 81.7439C0 122.334 32.9044 155.238 73.4939 155.238Z"
|
|
291
|
-
fill={`url(#${paint0})`}
|
|
292
|
-
/>
|
|
293
|
-
<path
|
|
294
|
-
d="M0.594727 90.9915C4.59951 122.921 29.0894 148.466 60.4966 154.085L102.462 116.355V102.302H86.8312L102.462 88.2489V74.1957H86.8312L102.462 60.1425V46.0894H50.5575L0.594727 90.9915Z"
|
|
295
|
-
fill="#BE1E6D"
|
|
296
|
-
/>
|
|
297
|
-
<path d="M102.474 116.355H50.5576L58.6764 102.302H102.474V116.355Z" fill="white" />
|
|
298
|
-
<path d="M102.474 60.1303H58.6764L50.5576 46.0771H102.474V60.1303Z" fill="white" />
|
|
299
|
-
<path d="M102.474 88.2368H66.7949L70.8483 81.2102L66.7949 74.1836H102.474V88.2368Z" fill="white" />
|
|
300
|
-
<path d="M39.2227 74.1835H66.795L58.6762 60.1304H39.2227V74.1835Z" fill="white" />
|
|
301
|
-
<path d="M39.2227 102.302H58.6762L66.795 88.2368H39.2227V102.302Z" fill="white" />
|
|
302
|
-
</g>
|
|
303
|
-
<g clipPath={`url(#${clip1})`}>
|
|
304
|
-
{/* Outer clip = SVG user space; inner translate avoids double-transforming the clip rect. */}
|
|
305
|
-
<g data-exxat-product-wordmark transform="translate(26 0)">
|
|
127
|
+
{/* ── "Exxat" letters — neutral slate on light, soft cool grey on dark ──
|
|
128
|
+
Filled via CSS class so dark-mode + `mutedSuffix` flips can override. */}
|
|
129
|
+
<g data-exxat-prefix>
|
|
306
130
|
<path
|
|
307
|
-
d="
|
|
308
|
-
|
|
309
|
-
/>
|
|
310
|
-
<path
|
|
311
|
-
d="M402.818 55.8327C405.052 55.5377 410.074 55.66 412.311 55.837C423.661 56.7359 434.899 61.6492 437.377 73.8645C438.215 77.9916 437.996 82.5469 437.985 86.8025C437.977 92.7617 437.929 98.7208 437.839 104.68C437.771 110.573 437.002 112.867 443.417 113.143L443.425 127.935C435.329 127.94 423.442 129.682 420.634 119.91C413.623 128.59 403.51 130.322 392.744 128.652C386.398 127.668 380.94 124.729 377.251 119.341C374.341 114.429 373.846 108.153 375.083 102.656C377.954 90.5343 391.037 87.8972 401.558 86.4666C405.949 85.8693 419.888 84.6536 420.496 78.9129C421.531 69.1633 405.427 66.9952 399.222 71.6421C395.538 74.4015 395.056 77.1527 394.214 81.4294C388.59 81.2 382.169 81.3447 376.488 81.3849C377.656 64.6911 386.55 57.7136 402.818 55.8327ZM394.722 112.842C402.622 116.994 414.879 113.956 419.195 105.867C420.898 102.675 420.786 100.037 420.753 96.5686C420.676 96.4397 420.464 96.0521 420.367 95.9825C417.57 96.9217 415.105 97.5589 412.227 98.1463C405.998 99.4177 386.104 100.755 393.813 111.851C394.008 112.132 394.46 112.588 394.722 112.842Z"
|
|
312
|
-
className={WM}
|
|
313
|
-
/>
|
|
314
|
-
<path
|
|
315
|
-
d="M285.842 57.092C288.383 57.0821 302.013 56.8534 303.49 57.3801C303.921 59.8946 296.385 70.3368 294.519 73.0942C290.393 79.1912 286.383 85.8209 282.086 91.7382L296.149 114.075C297.839 116.764 303.016 124.546 303.836 127.148C302.355 128.098 287.357 127.674 284.53 127.661C283.197 125.148 280.545 121.155 278.947 118.593C275.663 113.239 272.302 107.934 268.867 102.676C267.615 105.328 264.978 109.267 263.356 111.861L253.472 127.619C250.818 127.679 236.379 128.223 234.929 127.307C234.802 127.226 234.833 127.271 234.805 127.121C234.334 124.593 253.23 96.0195 256.079 91.7346C254.614 89.9792 253.162 87.6385 251.899 85.6936C246.461 77.3195 240.713 69.1346 235.418 60.6715C234.529 59.2505 234.261 58.7567 234.685 57.1032L252.113 57.0953C257.638 65.5363 263.657 74.0278 269.026 82.5146C273.446 74.8182 280.898 65.1741 285.842 57.092Z"
|
|
316
|
-
className={WM}
|
|
317
|
-
/>
|
|
318
|
-
<path
|
|
319
|
-
d="M305.863 57.0737L323.379 57.1011C329.02 65.2052 335.012 74.2679 340.362 82.5632C345.285 74.7113 352.033 64.6471 357.386 57.0948C361.996 57.0674 369.815 56.7925 374.173 57.1818C376.244 58.9665 368.54 69.0402 367.023 71.3256L353.506 91.742C356.918 98.694 374.121 122.433 374.944 127.689C368.802 127.534 362.114 127.674 355.93 127.682C351.193 119.662 345.363 110.633 340.337 102.74C335.343 110.592 329.601 119.589 324.992 127.585C318.896 127.832 311.626 127.673 305.417 127.677C306.982 123.265 310.578 118.366 313.063 114.31C317.652 106.819 322.836 99.2867 327.267 91.7074C321.78 83.8363 316.645 75.6089 311.249 67.6613C309.415 64.9596 305.794 60.2636 305.863 57.0737Z"
|
|
320
|
-
className={WM}
|
|
321
|
-
/>
|
|
322
|
-
<path
|
|
323
|
-
d="M454.026 36.0116C459.731 36.0271 465.436 36.0096 471.14 35.9585L471.16 57.0945L488.243 57.052L488.254 72.2463C482.683 72.1227 476.754 72.2229 471.155 72.2212L471.138 93.4004C471.135 97.6102 471.294 101.493 471.405 105.692C471.614 113.586 482.181 112.858 488.24 111.97L488.243 121.691L488.265 127.416C484.779 127.724 481.282 127.899 477.783 127.94C452.823 127.992 454.009 116.222 454.008 96.2313L454.009 72.2176C450.214 72.1859 446.419 72.2047 442.624 72.274C442.557 67.2104 442.563 62.1465 442.64 57.0833C443.23 57.0955 443.82 57.1018 444.409 57.1021C456.599 57.0942 454.076 44.6883 454.026 36.0116Z"
|
|
324
|
-
className={WM}
|
|
325
|
-
/>
|
|
326
|
-
<path
|
|
327
|
-
d="M709.341 59.4712C716.161 59.8473 722.388 60.0978 728.658 62.9393C728.596 67.6956 729.116 72.4767 728.948 77.2304C728.915 78.25 728.625 78.2575 727.805 78.5895C726.149 78.3568 725.164 76.1287 724.331 74.7855C720.494 68.5946 713.855 63.7545 706.235 65.229C703.018 65.852 701.173 67.4402 699.448 70.0608C699.27 72.2558 698.99 76.0269 700.63 77.7446C708.168 85.6309 720.708 87.2873 728.365 95.1211C735.62 102.545 734.454 115.729 726.821 122.426C719.902 128.588 710.773 129.464 701.895 129.132C698.341 128.594 689.667 127.399 686.796 125.738C685.518 124.267 684.932 110.419 684.816 107.618L685.324 107.085C686.041 107.047 686.789 107.004 687.286 107.65C693.165 115.302 696.79 123.714 708.043 123.502C713.075 123.407 720.214 119.506 719.134 114.29C717.023 104.1 698.266 101.216 691.511 94.1994C687.54 90.077 685.36 87.1363 684.928 81.2675C684.793 65.2695 695.615 60.3273 709.341 59.4712Z"
|
|
328
|
-
fill="#E31B79"
|
|
329
|
-
/>
|
|
330
|
-
<path
|
|
331
|
-
d="M618.055 70.3023C622.942 64.803 628.655 59.6719 636.542 60.0331C638.09 60.104 640.181 60.1933 641.587 60.8365C642.259 61.9866 638.782 76.3723 637.902 78.2548C635.943 78.7173 631.319 73.5997 628.408 73.0406C624.032 72.2007 621.786 73.8225 618.229 76.0014C618.437 79.0644 618.295 83.7093 618.288 86.8732L618.259 106.8C618.252 111.321 617.926 118.732 619.257 122.874C620.791 125.951 623.31 124.084 624.272 127.062L623.946 127.616C621.934 127.967 595.543 127.866 594.604 127.405C594.357 124.494 599.386 125.153 599.606 121.179C600.476 105.594 600.377 89.6685 599.732 74.0856C599.596 70.8189 593.995 67.4322 593.794 65.223C594.693 64.2789 614.389 60.52 617.165 60.0681L617.926 60.1713C618.796 61.2686 618.203 68.5635 618.055 70.3023Z"
|
|
332
|
-
fill="#E31B79"
|
|
131
|
+
d="M196 35.7646L235.626 35.811C239.705 35.8108 250.804 36.0941 254.421 35.6509L254.407 50.8756C240.766 50.8038 227.125 50.8041 213.485 50.877L213.495 74.3467C224.554 74.3448 238.413 74.7338 249.193 74.2652L249.203 89.7182C245.211 89.4232 239.525 89.5845 235.431 89.5872L213.496 89.6342L213.484 113.004L256.078 112.926L256.072 128.097C251.917 127.617 239.134 127.901 234.375 127.929C221.693 128.004 208.639 127.754 196 127.94V35.7646Z"
|
|
132
|
+
className="fill-[#273441] dark:fill-[#A8B2BA]"
|
|
333
133
|
/>
|
|
334
134
|
<path
|
|
335
|
-
d="
|
|
336
|
-
|
|
135
|
+
d="M311.843 57.1062C314.843 57.097 327.108 56.8601 329.377 57.2135L329.606 57.8503C329.333 60.3776 324.215 67.3436 322.511 69.9223C317.651 77.1875 312.847 84.4905 308.101 91.8305C309.118 93.8473 311.981 98.1472 313.27 100.2L323.962 117.113C325.904 120.182 329.179 124.459 329.551 127.99C323.658 127.748 316.574 127.94 310.593 127.946C307.782 122.799 304.084 117.694 301.099 112.597C299.196 109.347 296.932 105.769 294.706 102.746C293.772 104.889 290.7 109.57 289.357 111.724C285.993 117.165 282.58 122.576 279.12 127.956C276.595 127.908 261.817 128.243 260.671 127.615C260.249 126.007 261.797 123.527 262.702 122.205C269.332 112.519 275.107 101.261 281.979 91.8322C281.555 91.3258 281.153 90.8019 280.774 90.2617C279.987 89.1263 279.24 87.9296 278.486 86.7642C272.548 77.5965 266.263 68.6263 260.479 59.3668C260.192 58.9075 260.442 57.6507 260.539 57.1068C266.332 57.0478 272.125 57.0656 277.917 57.1596C283.454 64.9372 289.73 74.44 294.84 82.5018C296.784 80.011 299.246 76.0686 301.022 73.3917L311.843 57.1062Z"
|
|
136
|
+
className="fill-[#273441] dark:fill-[#A8B2BA]"
|
|
337
137
|
/>
|
|
338
138
|
<path
|
|
339
|
-
d="
|
|
340
|
-
|
|
139
|
+
d="M331.798 57.0712C337.592 57.1185 343.354 57.0072 349.156 57.1749C351.068 59.3357 353.387 63.1655 355.063 65.6344C358.847 71.2087 362.432 77.014 366.36 82.4907C370.845 75.0665 378.273 64.1367 383.331 57.1032C385.626 57.0945 399.689 56.8398 400.866 57.3117C401.39 58.6028 399.759 61.1078 399.008 62.1658C392.221 71.7261 386.21 82.4699 379.267 91.8592C383.35 97.6717 387.268 104.526 391.166 110.532C393.16 113.606 400.626 124.783 400.949 127.559C399.879 128.243 384.096 127.95 382.001 127.951C377.115 119.684 371.356 110.895 366.217 102.718C364.993 105.113 362.366 109.016 360.85 111.436C357.401 116.922 353.992 122.433 350.622 127.968C348.242 127.897 332.898 128.279 332.168 127.57C332.118 126.754 332.069 125.826 332.449 125.099C334.495 121.173 337.285 117.058 339.661 113.307L353.193 91.8005C352.418 90.7063 351.628 89.4998 350.898 88.362C344.705 78.7208 337.866 69.3865 332.081 59.5087C331.713 58.8803 331.753 57.7841 331.798 57.0712Z"
|
|
140
|
+
className="fill-[#273441] dark:fill-[#A8B2BA]"
|
|
341
141
|
/>
|
|
342
142
|
<path
|
|
343
|
-
d="
|
|
344
|
-
|
|
143
|
+
d="M430.755 55.7344C443.603 55.2553 459.709 58.3988 463.175 73.143C464.166 77.3587 463.884 82.6952 463.877 87.0408L463.845 105.91C463.857 112.149 463.051 112.645 469.328 113.205C469.057 117.66 469.234 123.628 469.242 128.194C461.169 128.149 448.96 129.818 446.764 119.668C444.471 122.422 443.567 123.605 440.357 125.604C433.884 129.636 423.423 129.934 416.178 128.172C410.382 126.762 405.62 123.519 402.512 118.29C400.527 114.233 400.122 109.477 400.649 105.072C402.505 89.5606 418.762 87.5983 431.172 85.928C435.522 85.237 440.828 84.6483 444.472 82.0073C447.548 79.7708 447.168 76.525 444.97 73.7922C440.68 68.4582 429.516 68.0981 424.356 72.2079C421.359 74.5939 420.834 77.8667 420.5 81.4362C414.437 81.3803 408.373 81.3842 402.31 81.4478C402.503 79.5217 402.653 77.3957 403.031 75.5059C405.774 61.7822 418.095 56.4115 430.755 55.7344ZM420.852 112.903C428.033 116.948 440.992 113.872 444.937 106.311C445.852 104.557 447.932 97.6766 446.702 95.9674L446.344 95.9145C442.708 97.4881 435.913 98.8678 431.795 99.3372C425.343 100.072 411.452 104.687 420.852 112.903Z"
|
|
144
|
+
className="fill-[#273441] dark:fill-[#A8B2BA]"
|
|
345
145
|
/>
|
|
346
146
|
<path
|
|
347
|
-
d="
|
|
348
|
-
className=
|
|
147
|
+
d="M479.835 35.8541C485.68 35.8897 491.521 35.8895 497.366 35.853C497.152 42.4806 497.332 50.2661 497.324 56.9807L514.284 56.9509L514.292 72.0978C508.643 72.0524 502.993 72.036 497.341 72.0485L497.313 93.563C497.313 97.0693 496.581 108.123 499.41 110.474C502.231 112.82 510.459 112.62 514.295 112.135L514.279 123.529L514.292 127.437C511.116 127.895 507.911 128.146 504.704 128.188C479.71 128.491 479.891 117.266 479.916 96.9484C479.949 88.6621 479.938 80.3759 479.88 72.09C476.431 72.026 471.882 71.9331 468.516 72.1906C468.292 67.7063 468.488 61.552 468.523 56.9865C469.391 56.999 470.258 56.9979 471.126 56.9829C482.417 56.741 480.091 43.7789 479.835 35.8541Z"
|
|
148
|
+
className="fill-[#273441] dark:fill-[#A8B2BA]"
|
|
349
149
|
/>
|
|
350
|
-
</g>
|
|
351
150
|
</g>
|
|
151
|
+
|
|
352
152
|
<defs>
|
|
353
153
|
<linearGradient
|
|
354
|
-
id={
|
|
154
|
+
id={gradId}
|
|
355
155
|
x1="28.3733"
|
|
356
156
|
y1="134.255"
|
|
357
157
|
x2="117.195"
|
|
358
158
|
y2="30.9074"
|
|
359
159
|
gradientUnits="userSpaceOnUse"
|
|
360
160
|
>
|
|
361
|
-
<stop offset="0
|
|
362
|
-
<stop offset="
|
|
363
|
-
<stop offset="0.73" stopColor="#E42880" />
|
|
364
|
-
<stop offset="0.88" stopColor="#E9448E" />
|
|
365
|
-
<stop offset="1" stopColor="#EF609D" />
|
|
161
|
+
<stop offset="0" stopColor={from} />
|
|
162
|
+
<stop offset="1" stopColor={to} />
|
|
366
163
|
</linearGradient>
|
|
367
|
-
<clipPath id={clip0}>
|
|
368
|
-
<rect width="147" height="147" fill="white" transform="translate(0 8.25)" />
|
|
369
|
-
</clipPath>
|
|
370
|
-
<clipPath id={clip1}>
|
|
371
|
-
{/* Same x=196 window as Exxat One wordmark; group translate(26) aligns Prism ink. */}
|
|
372
|
-
<rect width="675" height="163.5" fill="white" transform="translate(196)" />
|
|
373
|
-
</clipPath>
|
|
374
164
|
</defs>
|
|
375
165
|
</svg>
|
|
376
166
|
)
|
|
377
167
|
}
|
|
378
168
|
|
|
379
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Mark + wordmark composed inline. Sizing is height-driven via `className`
|
|
171
|
+
* (e.g. `h-9`) — the SVG base scales with the parent height, and the suffix
|
|
172
|
+
* text scales with the parent `text-base` (via `text-[1.8em]`) so its
|
|
173
|
+
* cap-height matches the SVG "Exxat" cap.
|
|
174
|
+
*
|
|
175
|
+
* **Geometry at h-9 (36 px) parent:**
|
|
176
|
+
* - SVG `0 0 514 164` → renders 113 × 36 px, scale 0.2195
|
|
177
|
+
* - Mark height: 147 × 0.2195 ≈ 32 px (matches school Avatar slot)
|
|
178
|
+
* - "Exxat" cap height: 92 × 0.2195 ≈ 20 px (sits centred on mark midline)
|
|
179
|
+
* - "Exxat" baseline: y=128 in viewBox → ≈ 28.1 px from SVG top
|
|
180
|
+
*
|
|
181
|
+
* **Suffix sizing:** `text-[1.8em]` of 16 px base → 28.8 px font, cap ≈ 20 px
|
|
182
|
+
* (matches "Exxat" cap). `translate-y-[0.05em]` (~1.4 px down) corrects the
|
|
183
|
+
* residual cap-midpoint offset between HTML text metrics and the SVG baseline
|
|
184
|
+
* authored at y=128.
|
|
185
|
+
*/
|
|
186
|
+
export function ExxatProductLogo({
|
|
187
|
+
product,
|
|
188
|
+
className,
|
|
189
|
+
variant = "default",
|
|
190
|
+
}: ExxatProductLogoProps) {
|
|
191
|
+
const customProductBrand = useAppStore(s => s.customProductBrand)
|
|
192
|
+
const productBrandColors = useAppStore(s => s.productBrandColors)
|
|
193
|
+
const config = brandForProduct(product, customProductBrand, productBrandColors)
|
|
194
|
+
const muted = variant === "mutedSuffix"
|
|
195
|
+
const suffixColor = config.wordmarkColor ?? config.brandColor
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<span
|
|
199
|
+
aria-hidden="true"
|
|
200
|
+
data-product-logo
|
|
201
|
+
data-product-id={config.id}
|
|
202
|
+
className={cn(
|
|
203
|
+
"inline-flex items-end overflow-visible text-base leading-none",
|
|
204
|
+
className,
|
|
205
|
+
)}
|
|
206
|
+
>
|
|
207
|
+
{config.compactLogo ? (
|
|
208
|
+
<ProductMark config={config} className="size-7" />
|
|
209
|
+
) : (
|
|
210
|
+
<ExxatLogoBase config={config} style={{ height: 28, width: "auto" }} />
|
|
211
|
+
)}
|
|
212
|
+
|
|
213
|
+
{/* HTML suffix — IvyPresto Text SemiBold per Figma brand spec. */}
|
|
214
|
+
<span
|
|
215
|
+
data-product-wordmark-suffix
|
|
216
|
+
className={cn(
|
|
217
|
+
"ms-[0.18em] text-[1.55em] font-semibold tracking-[-0.03em] -translate-y-[3px]",
|
|
218
|
+
muted && "dark:!text-[var(--muted-foreground)]",
|
|
219
|
+
)}
|
|
220
|
+
style={{
|
|
221
|
+
fontFamily: "var(--font-heading), 'ivypresto-text', Georgia, serif",
|
|
222
|
+
color: suffixColor,
|
|
223
|
+
}}
|
|
224
|
+
>
|
|
225
|
+
{config.suffix}
|
|
226
|
+
</span>
|
|
227
|
+
</span>
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface ExxatProductMarkProps {
|
|
380
232
|
product: Product
|
|
381
233
|
className?: string
|
|
382
|
-
|
|
383
|
-
variant?: ExxatProductLogoVariant
|
|
234
|
+
cutoutColor?: string
|
|
384
235
|
}
|
|
385
236
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Circular mark only — collapsed sidebar (matches Avatar 32×32). Reuses the
|
|
239
|
+
* generic `ProductMark` because the mark's geometry is identical across all
|
|
240
|
+
* Exxat products; only colours change per brand.
|
|
241
|
+
*/
|
|
242
|
+
export function ExxatProductMark({ product, className, cutoutColor }: ExxatProductMarkProps) {
|
|
243
|
+
const customProductBrand = useAppStore(s => s.customProductBrand)
|
|
244
|
+
const productBrandColors = useAppStore(s => s.productBrandColors)
|
|
245
|
+
const config = brandForProduct(product, customProductBrand, productBrandColors)
|
|
246
|
+
return <ProductMark config={config} className={className} cutoutColor={cutoutColor} />
|
|
392
247
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
|
-
import { useForm } from "react-hook-form"
|
|
4
|
+
import { useForm, useWatch } from "react-hook-form"
|
|
5
5
|
import { z } from "zod"
|
|
6
6
|
import { zodResolver } from "@hookform/resolvers/zod"
|
|
7
7
|
|
|
@@ -191,7 +191,9 @@ export function InviteCollaboratorsDrawer({
|
|
|
191
191
|
access: "editor",
|
|
192
192
|
},
|
|
193
193
|
})
|
|
194
|
-
|
|
194
|
+
// `useWatch` is memoization-friendly (returns a stable reactive value)
|
|
195
|
+
// unlike `form.watch()`, which the React Compiler can't memoize safely.
|
|
196
|
+
const inviteAccess = useWatch({ control: form.control, name: "access" })
|
|
195
197
|
const [isSubmitting, setIsSubmitting] = React.useState(false)
|
|
196
198
|
const [removeTarget, setRemoveTarget] = React.useState<PageHeaderCollaborator | null>(null)
|
|
197
199
|
|
|
@@ -226,7 +228,7 @@ export function InviteCollaboratorsDrawer({
|
|
|
226
228
|
side="right"
|
|
227
229
|
showCloseButton={false}
|
|
228
230
|
showOverlay={false}
|
|
229
|
-
className="w-80 sm:max-w-80 p-0 gap-0 flex flex-col border border-border shadow-xl rounded-xl"
|
|
231
|
+
className="z-[80] w-80 sm:max-w-80 p-0 gap-0 flex flex-col border border-border shadow-xl rounded-xl"
|
|
230
232
|
style={{ top: "0.5rem", bottom: "0.5rem", right: "0.5rem", height: "calc(100vh - 1rem)" }}
|
|
231
233
|
onPointerDownOutside={event => {
|
|
232
234
|
if (isOverlaySelectorSheetTarget(event.target)) {
|