@the-portland-company/shell 0.89.0 → 0.90.0

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.
@@ -76,9 +76,11 @@ type PageHeaderProps = {
76
76
  showEyebrow?: boolean;
77
77
  /** Optional one-line subtitle, 15px G4, directly under the title. */
78
78
  subtitle?: ReactNode;
79
+ /** @deprecated The breadcrumb trail is retired. Accepted for compatibility; renders nothing and warns once in dev. */
79
80
  breadcrumbs?: BreadcrumbItem$1[];
80
81
  actions?: ReactNode;
81
82
  tabBar?: ReactNode;
83
+ /** @deprecated Ignored. The header spans its container; pages choose `FormColumn` (960px) or the fluid frame. */
82
84
  maxW?: string;
83
85
  hideSidebar?: boolean;
84
86
  };
@@ -246,7 +248,7 @@ type FileInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'valu
246
248
  * field tokens as `Input`; the native "Choose file" button is styled with the
247
249
  * secondary-button tokens. Use `accept` / `multiple` as usual.
248
250
  */
249
- declare const FileInput: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "type" | "defaultValue"> & FieldChromeProps & {
251
+ declare const FileInput: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "defaultValue"> & FieldChromeProps & {
250
252
  /** Class for the control itself; the wrapper takes `wrapperClassName`. */
251
253
  className?: string;
252
254
  wrapperClassName?: string;
@@ -689,7 +691,7 @@ declare function useThemePreference(): {
689
691
  setValue: (next: ThemePreference) => void;
690
692
  };
691
693
 
692
- /** Page header for apps with no react-router: breadcrumbs render as plain anchors. */
694
+ /** Page header for apps with no react-router. Same view as the main entry. */
693
695
  declare function PageHeader(props: PageHeaderProps): react_jsx_runtime.JSX.Element;
694
696
 
695
697
  /**
@@ -76,9 +76,11 @@ type PageHeaderProps = {
76
76
  showEyebrow?: boolean;
77
77
  /** Optional one-line subtitle, 15px G4, directly under the title. */
78
78
  subtitle?: ReactNode;
79
+ /** @deprecated The breadcrumb trail is retired. Accepted for compatibility; renders nothing and warns once in dev. */
79
80
  breadcrumbs?: BreadcrumbItem$1[];
80
81
  actions?: ReactNode;
81
82
  tabBar?: ReactNode;
83
+ /** @deprecated Ignored. The header spans its container; pages choose `FormColumn` (960px) or the fluid frame. */
82
84
  maxW?: string;
83
85
  hideSidebar?: boolean;
84
86
  };
@@ -246,7 +248,7 @@ type FileInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'valu
246
248
  * field tokens as `Input`; the native "Choose file" button is styled with the
247
249
  * secondary-button tokens. Use `accept` / `multiple` as usual.
248
250
  */
249
- declare const FileInput: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "type" | "defaultValue"> & FieldChromeProps & {
251
+ declare const FileInput: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "defaultValue"> & FieldChromeProps & {
250
252
  /** Class for the control itself; the wrapper takes `wrapperClassName`. */
251
253
  className?: string;
252
254
  wrapperClassName?: string;
@@ -689,7 +691,7 @@ declare function useThemePreference(): {
689
691
  setValue: (next: ThemePreference) => void;
690
692
  };
691
693
 
692
- /** Page header for apps with no react-router: breadcrumbs render as plain anchors. */
694
+ /** Page header for apps with no react-router. Same view as the main entry. */
693
695
  declare function PageHeader(props: PageHeaderProps): react_jsx_runtime.JSX.Element;
694
696
 
695
697
  /**
package/dist/index.cjs CHANGED
@@ -3,8 +3,8 @@
3
3
  var react = require('@chakra-ui/react');
4
4
  var clsx = require('clsx');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
- var reactRouterDom = require('react-router-dom');
7
6
  var react$1 = require('react');
7
+ var reactRouterDom = require('react-router-dom');
8
8
  require('react-dom');
9
9
 
10
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -906,65 +906,85 @@ function Card({
906
906
  function FormColumn({ children, className }) {
907
907
  return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-shell-form-column": "", className: clsx__default.default("w-full max-w-[960px]", className), children });
908
908
  }
909
+
910
+ // src/lib/deprecation.ts
911
+ var warned = /* @__PURE__ */ new Set();
912
+ function isDev() {
913
+ try {
914
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
915
+ } catch {
916
+ return false;
917
+ }
918
+ }
919
+ function warnDeprecated(key, message) {
920
+ if (!isDev() || warned.has(key)) return;
921
+ warned.add(key);
922
+ console.warn(`[@the-portland-company/shell] ${message}`);
923
+ }
909
924
  var MODE_EYEBROW_LABEL = {
910
925
  relationship: "Relationship mode",
911
926
  campaign: "Campaign mode",
912
927
  petition: "Petition mode"
913
928
  };
914
- var resolveMaxWidthClass = (maxW) => {
915
- if (maxW === "full") return "max-w-none";
916
- if (maxW === "container.lg") return "max-w-6xl";
917
- if (maxW === "container.md") return "max-w-5xl";
918
- return "max-w-7xl";
919
- };
929
+ var EMAIL_PATTERN = /[^\s@]+@[^\s@]+\.[^\s@]+/;
920
930
  function PageHeaderView({
921
- renderCrumbLink,
922
931
  title,
923
932
  mode,
924
933
  eyebrow,
925
934
  showEyebrow = true,
926
935
  subtitle,
927
- breadcrumbs = [],
936
+ breadcrumbs,
928
937
  actions = null,
929
938
  tabBar = null,
930
- maxW = "full",
939
+ maxW,
931
940
  hideSidebar = false
932
941
  }) {
933
- const maxWClass = resolveMaxWidthClass(maxW);
934
- const titleText = typeof title === "string" ? title.trim() : "";
935
- const normalizedTitle = titleText.toLowerCase();
936
- const breadcrumbsWithoutCurrent = [...breadcrumbs];
937
- const last = breadcrumbsWithoutCurrent[breadcrumbsWithoutCurrent.length - 1];
938
- if (breadcrumbsWithoutCurrent.length > 1 && normalizedTitle && last && last.label.trim().toLowerCase() === normalizedTitle) {
939
- breadcrumbsWithoutCurrent.pop();
942
+ if (breadcrumbs !== void 0) {
943
+ warnDeprecated(
944
+ "PageHeader.breadcrumbs",
945
+ '<PageHeader breadcrumbs> is deprecated and renders nothing: the breadcrumb trail is retired (Reference/UI-Platform \u2192 "Page header"). Remove the prop.'
946
+ );
947
+ }
948
+ if (maxW !== void 0) {
949
+ warnDeprecated(
950
+ "PageHeader.maxW",
951
+ "<PageHeader maxW> is deprecated and ignored: the header spans its container. Use <FormColumn> (960px) or the fluid frame for page width."
952
+ );
953
+ }
954
+ if (typeof title === "string" && EMAIL_PATTERN.test(title) || typeof subtitle === "string" && EMAIL_PATTERN.test(subtitle)) {
955
+ warnDeprecated(
956
+ "PageHeader.email",
957
+ `<PageHeader> title/subtitle contains an email address. The user's email never appears in the page header (Reference/UI-Platform \u2192 "Page header"); identity lives in the left bar footer.`
958
+ );
940
959
  }
941
960
  const eyebrowText = showEyebrow ? eyebrow ?? (mode ? MODE_EYEBROW_LABEL[mode] : void 0) : void 0;
942
- if (!title && !eyebrowText && breadcrumbsWithoutCurrent.length === 0 && !actions && !tabBar) {
961
+ if (!title && !eyebrowText && !actions && !tabBar) {
943
962
  return null;
944
963
  }
945
964
  return /* @__PURE__ */ jsxRuntime.jsxs(
946
965
  "div",
947
966
  {
948
- style: { backgroundColor: "var(--app-tab-header-bg, #888888)" },
949
- className: clsx__default.default("text-slate-100", tabBar ? "pt-3 pb-0" : "py-3 border-b border-gray-200", {
967
+ "data-shell-page-header": "",
968
+ className: clsx__default.default("pt-[22px] text-[color:var(--color-text,#2F3136)]", tabBar ? "pb-0" : "pb-4", {
950
969
  "md:ml-60": !hideSidebar
951
970
  }),
952
971
  children: [
953
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx__default.default("w-full px-4", maxWClass), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full flex-nowrap items-start justify-between gap-4", children: [
972
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full px-4 md:px-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full flex-nowrap items-start justify-between gap-4", children: [
954
973
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
955
974
  eyebrowText ? /* @__PURE__ */ jsxRuntime.jsx(
956
975
  "div",
957
976
  {
958
977
  className: "truncate text-[11px] font-bold uppercase leading-[1.2] tracking-[0.09em]",
959
- style: { color: "var(--mode-accent, var(--color-accent, #E63946))" },
978
+ style: { color: "var(--color-accent-fg, var(--mode-accent, var(--color-accent, #E63946)))" },
960
979
  children: eyebrowText
961
980
  }
962
981
  ) : null,
963
982
  title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
964
- "div",
983
+ "h1",
965
984
  {
966
- className: "truncate font-bold text-white",
985
+ className: "m-0 truncate font-bold",
967
986
  style: {
987
+ color: "var(--color-text, #2F3136)",
968
988
  fontFamily: "var(--font-family, ui-sans-serif, system-ui, sans-serif)",
969
989
  fontSize: "var(--type-h2-size, 1.875rem)",
970
990
  lineHeight: "var(--type-h2-line-height, 1.25)"
@@ -972,25 +992,7 @@ function PageHeaderView({
972
992
  children: title
973
993
  }
974
994
  ) }) : null,
975
- subtitle ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 truncate text-[15px] text-[color:var(--color-g4,#929599)]", children: subtitle }) : null,
976
- breadcrumbsWithoutCurrent.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
977
- "nav",
978
- {
979
- "aria-label": "Breadcrumb",
980
- className: clsx__default.default("min-w-0 text-slate-100", title ? "mt-1" : ""),
981
- children: /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "flex flex-nowrap items-center gap-2 overflow-hidden text-sm text-gray-600", children: breadcrumbsWithoutCurrent.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
982
- "li",
983
- {
984
- className: "flex min-w-0 flex-shrink-0 items-center gap-2",
985
- children: [
986
- index > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: ">" }) : null,
987
- item.href ? renderCrumbLink(item.href, item.label, "truncate text-slate-100 hover:underline") : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-slate-100", children: item.label })
988
- ]
989
- },
990
- `${item.label}-${index}`
991
- )) })
992
- }
993
- ) : null
995
+ subtitle ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 truncate text-[15px] text-[color:var(--color-g4,#58657A)]", children: subtitle }) : null
994
996
  ] }),
995
997
  actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: actions }) : null
996
998
  ] }) }),
@@ -1000,13 +1002,7 @@ function PageHeaderView({
1000
1002
  );
1001
1003
  }
1002
1004
  function PageHeader(props) {
1003
- return /* @__PURE__ */ jsxRuntime.jsx(
1004
- PageHeaderView,
1005
- {
1006
- ...props,
1007
- renderCrumbLink: (href, label, className) => /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: href, className, children: label })
1008
- }
1009
- );
1005
+ return /* @__PURE__ */ jsxRuntime.jsx(PageHeaderView, { ...props });
1010
1006
  }
1011
1007
  var VARIANT_CLASS = {
1012
1008
  primary: "border border-transparent bg-[color:var(--app-btn-primary-bg,var(--color-accent))] text-[color:var(--app-btn-primary-fg,var(--color-white))] hover:bg-[color:var(--app-btn-primary-bg-hover,var(--color-accent-hover))]",
@@ -1313,21 +1309,6 @@ var FileInput = react$1.forwardRef(function FileInput2({ label, helperText, erro
1313
1309
  );
1314
1310
  });
1315
1311
  var FileInput_default = FileInput;
1316
-
1317
- // src/lib/deprecation.ts
1318
- var warned = /* @__PURE__ */ new Set();
1319
- function isDev() {
1320
- try {
1321
- return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1322
- } catch {
1323
- return false;
1324
- }
1325
- }
1326
- function warnDeprecated(key, message) {
1327
- if (!isDev() || warned.has(key)) return;
1328
- warned.add(key);
1329
- console.warn(`[@the-portland-company/shell] ${message}`);
1330
- }
1331
1312
  function PageContainer({
1332
1313
  children,
1333
1314
  maxW = "container.xl",