@aglyn/shared-ui-next 1.0.0-beta.143
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/LICENSE +201 -0
- package/README.md +7 -0
- package/package.json +45 -0
- package/src/index.d.ts +17 -0
- package/src/index.js +18 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/hub-tabs.d.ts +104 -0
- package/src/lib/components/hub-tabs.js +292 -0
- package/src/lib/components/hub-tabs.js.map +1 -0
- package/src/lib/components/image.d.ts +77 -0
- package/src/lib/components/image.js +75 -0
- package/src/lib/components/image.js.map +1 -0
- package/src/lib/components/page-decorated.d.ts +46 -0
- package/src/lib/components/page-decorated.js +47 -0
- package/src/lib/components/page-decorated.js.map +1 -0
- package/src/lib/contexts/next-page-title-provider.d.ts +40 -0
- package/src/lib/contexts/next-page-title-provider.js +81 -0
- package/src/lib/contexts/next-page-title-provider.js.map +1 -0
- package/src/lib/hooks/use-tab-param.d.ts +58 -0
- package/src/lib/hooks/use-tab-param.js +70 -0
- package/src/lib/hooks/use-tab-param.js.map +1 -0
- package/src/lib/next.d.ts +20 -0
- package/src/lib/next.js +26 -0
- package/src/lib/next.js.map +1 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type PageTitleObject } from '@aglyn/shared-util-dom';
|
|
18
|
+
export interface NextPageTitleContextValue {
|
|
19
|
+
title: JSX.Node;
|
|
20
|
+
setScreenNumber(value?: number): void;
|
|
21
|
+
setScreenName(value: JSX.Node, page?: number): void;
|
|
22
|
+
setScreenSuffix(value: JSX.Node): void;
|
|
23
|
+
setScreenSeparator(value: string): void;
|
|
24
|
+
setScreenTitle(values: PageTitleObject): any;
|
|
25
|
+
}
|
|
26
|
+
export declare const NextPageTitleContext: import("react").Context<NextPageTitleContextValue>;
|
|
27
|
+
export declare const useNextPageTitleContext: () => NextPageTitleContextValue;
|
|
28
|
+
export declare const useNextPageTitle: (values: PageTitleObject) => import("react").ReactNode;
|
|
29
|
+
export interface NextPageTitleProps extends PageTitleObject {
|
|
30
|
+
children?: JSX.Children;
|
|
31
|
+
}
|
|
32
|
+
export declare const NextPageTitle: (props: NextPageTitleProps) => import("react").JSX.Element;
|
|
33
|
+
export interface NextPageTitleContextProps {
|
|
34
|
+
children?: JSX.Children;
|
|
35
|
+
}
|
|
36
|
+
export declare function NextPageTitleProvider(props: NextPageTitleContextProps): import("react").JSX.Element;
|
|
37
|
+
export declare namespace NextPageTitleProvider {
|
|
38
|
+
var displayName: string;
|
|
39
|
+
}
|
|
40
|
+
export default NextPageTitleProvider;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2023 Aglyn LLC
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/ import { BRAND } from "@aglyn/shared-data-enums";
|
|
19
|
+
import { useSubscribable } from "@aglyn/shared-ui-jsx";
|
|
20
|
+
import { $pageTitle, setScreenName, setScreenNumber, setScreenSeparator, setScreenSuffix, setScreenTitle } from "@aglyn/shared-util-dom";
|
|
21
|
+
import Head from "next/head";
|
|
22
|
+
import { createContext, useContext, useEffect, useMemo } from "react";
|
|
23
|
+
export const NextPageTitleContext = /*#__PURE__*/ createContext({
|
|
24
|
+
title: `${BRAND.ORG_NAME} App`,
|
|
25
|
+
setScreenName,
|
|
26
|
+
setScreenSeparator,
|
|
27
|
+
setScreenSuffix,
|
|
28
|
+
setScreenNumber,
|
|
29
|
+
setScreenTitle
|
|
30
|
+
});
|
|
31
|
+
export const useNextPageTitleContext = ()=>{
|
|
32
|
+
return useContext(NextPageTitleContext);
|
|
33
|
+
};
|
|
34
|
+
export const useNextPageTitle = (values)=>{
|
|
35
|
+
const { title, setScreenTitle } = useNextPageTitleContext();
|
|
36
|
+
useEffect(()=>{
|
|
37
|
+
setScreenTitle(values);
|
|
38
|
+
}, [
|
|
39
|
+
values,
|
|
40
|
+
setScreenTitle
|
|
41
|
+
]);
|
|
42
|
+
return title;
|
|
43
|
+
};
|
|
44
|
+
export const NextPageTitle = (props)=>{
|
|
45
|
+
const { children } = props, rest = _object_without_properties_loose(props, [
|
|
46
|
+
"children"
|
|
47
|
+
]);
|
|
48
|
+
useNextPageTitle(rest);
|
|
49
|
+
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
50
|
+
children: children
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
export function NextPageTitleProvider(props) {
|
|
54
|
+
const { children } = props;
|
|
55
|
+
const title = useSubscribable($pageTitle);
|
|
56
|
+
const state = useMemo(()=>({
|
|
57
|
+
title,
|
|
58
|
+
setScreenName,
|
|
59
|
+
setScreenSeparator,
|
|
60
|
+
setScreenSuffix,
|
|
61
|
+
setScreenNumber,
|
|
62
|
+
setScreenTitle
|
|
63
|
+
}), [
|
|
64
|
+
title
|
|
65
|
+
]);
|
|
66
|
+
return /*#__PURE__*/ _jsxs(NextPageTitleContext.Provider, {
|
|
67
|
+
value: state,
|
|
68
|
+
children: [
|
|
69
|
+
/*#__PURE__*/ _jsx(Head, {
|
|
70
|
+
children: /*#__PURE__*/ _jsx("title", {
|
|
71
|
+
children: title
|
|
72
|
+
})
|
|
73
|
+
}),
|
|
74
|
+
children
|
|
75
|
+
]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
NextPageTitleProvider.displayName = 'NextPageTitleComponent';
|
|
79
|
+
export default NextPageTitleProvider;
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=next-page-title-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/next/src/lib/contexts/next-page-title-provider.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2023 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BRAND } from '@aglyn/shared-data-enums'\nimport { useSubscribable } from '@aglyn/shared-ui-jsx'\nimport {\n $pageTitle,\n type PageTitleObject,\n setScreenName,\n setScreenNumber,\n setScreenSeparator,\n setScreenSuffix,\n setScreenTitle,\n} from '@aglyn/shared-util-dom'\nimport Head from 'next/head'\nimport { createContext, useContext, useEffect, useMemo } from 'react'\n\nexport interface NextPageTitleContextValue {\n title: JSX.Node\n setScreenNumber(value?: number): void\n setScreenName(value: JSX.Node, page?: number): void\n setScreenSuffix(value: JSX.Node): void\n setScreenSeparator(value: string): void\n setScreenTitle(values: PageTitleObject)\n}\n\nexport const NextPageTitleContext = createContext<NextPageTitleContextValue>({\n title: `${BRAND.ORG_NAME} App`,\n setScreenName,\n setScreenSeparator,\n setScreenSuffix,\n setScreenNumber,\n setScreenTitle,\n})\n\nexport const useNextPageTitleContext = () => {\n return useContext(NextPageTitleContext)\n}\n\nexport const useNextPageTitle = (values: PageTitleObject) => {\n const { title, setScreenTitle } = useNextPageTitleContext()\n\n useEffect(() => {\n setScreenTitle(values)\n }, [values, setScreenTitle])\n\n return title\n}\n\nexport interface NextPageTitleProps extends PageTitleObject {\n children?: JSX.Children\n}\n\nexport const NextPageTitle = (props: NextPageTitleProps) => {\n const { children, ...rest } = props\n\n useNextPageTitle(rest)\n\n return <>{children}</>\n}\n\nexport interface NextPageTitleContextProps {\n children?: JSX.Children\n}\n\nexport function NextPageTitleProvider(props: NextPageTitleContextProps) {\n const { children } = props\n\n const title = useSubscribable($pageTitle)\n const state = useMemo(\n () => ({\n title,\n setScreenName,\n setScreenSeparator,\n setScreenSuffix,\n setScreenNumber,\n setScreenTitle,\n }),\n [title],\n )\n\n return (\n <NextPageTitleContext.Provider value={state}>\n <Head>\n <title>{title}</title>\n </Head>\n {children}\n </NextPageTitleContext.Provider>\n )\n}\nNextPageTitleProvider.displayName = 'NextPageTitleComponent'\n\nexport default NextPageTitleProvider\n"],"names":["BRAND","useSubscribable","$pageTitle","setScreenName","setScreenNumber","setScreenSeparator","setScreenSuffix","setScreenTitle","Head","createContext","useContext","useEffect","useMemo","NextPageTitleContext","title","ORG_NAME","useNextPageTitleContext","useNextPageTitle","values","NextPageTitle","props","children","rest","NextPageTitleProvider","state","Provider","value","displayName"],"mappings":";;AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,KAAK,QAAQ,2BAA0B;AAChD,SAASC,eAAe,QAAQ,uBAAsB;AACtD,SACEC,UAAU,EAEVC,aAAa,EACbC,eAAe,EACfC,kBAAkB,EAClBC,eAAe,EACfC,cAAc,QACT,yBAAwB;AAC/B,OAAOC,UAAU,YAAW;AAC5B,SAASC,aAAa,EAAEC,UAAU,EAAEC,SAAS,EAAEC,OAAO,QAAQ,QAAO;AAWrE,OAAO,MAAMC,qCAAuBJ,cAAyC;IAC3EK,OAAO,GAAGd,MAAMe,QAAQ,CAAC,IAAI,CAAC;IAC9BZ;IACAE;IACAC;IACAF;IACAG;AACF,GAAE;AAEF,OAAO,MAAMS,0BAA0B;IACrC,OAAON,WAAWG;AACpB,EAAC;AAED,OAAO,MAAMI,mBAAmB,CAACC;IAC/B,MAAM,EAAEJ,KAAK,EAAEP,cAAc,EAAE,GAAGS;IAElCL,UAAU;QACRJ,eAAeW;IACjB,GAAG;QAACA;QAAQX;KAAe;IAE3B,OAAOO;AACT,EAAC;AAMD,OAAO,MAAMK,gBAAgB,CAACC;IAC5B,MAAM,EAAEC,QAAQ,EAAW,GAAGD,OAATE,wCAASF;;;IAE9BH,iBAAiBK;IAEjB,qBAAO;kBAAGD;;AACZ,EAAC;AAMD,OAAO,SAASE,sBAAsBH,KAAgC;IACpE,MAAM,EAAEC,QAAQ,EAAE,GAAGD;IAErB,MAAMN,QAAQb,gBAAgBC;IAC9B,MAAMsB,QAAQZ,QACZ,IAAO,CAAA;YACLE;YACAX;YACAE;YACAC;YACAF;YACAG;QACF,CAAA,GACA;QAACO;KAAM;IAGT,qBACE,MAACD,qBAAqBY,QAAQ;QAACC,OAAOF;;0BACpC,KAAChB;0BACC,cAAA,KAACM;8BAAOA;;;YAETO;;;AAGP;AACAE,sBAAsBI,WAAW,GAAG;AAEpC,eAAeJ,sBAAqB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `?tab=` ↔ the selected vertical tab, in one place (AGL-2486, AGL-2501).
|
|
3
|
+
*
|
|
4
|
+
* Every surface with vertical tabs deep-links the same way, and it is this
|
|
5
|
+
* hook that makes that true. Left to themselves, three pages produce three
|
|
6
|
+
* different answers and one of them is wrong: validating the incoming id
|
|
7
|
+
* against a HAND-MAINTAINED LIST silently opens the default tab for any id
|
|
8
|
+
* added later; validating against two hardcoded ids is correct only until a
|
|
9
|
+
* third arrives; and reading `window.location.search` once on mount ignores
|
|
10
|
+
* every later navigation.
|
|
11
|
+
*
|
|
12
|
+
* ## Why this lives in the library rather than in the console
|
|
13
|
+
*
|
|
14
|
+
* The console is not the only surface with a vertical tab rail. `HubTabs` —
|
|
15
|
+
* the rail itself — is shared, and every relocated feature plugin's console
|
|
16
|
+
* page renders through it. A library cannot import from an app, so a hook
|
|
17
|
+
* that lived in `apps/console/hooks` was one the rail could not use, and the
|
|
18
|
+
* rail therefore grew the fourth answer: it read the parameter once into
|
|
19
|
+
* `useState` and never looked again, so back, forward and an in-app link into
|
|
20
|
+
* another section of an open page all left the rail on the old tab while the
|
|
21
|
+
* URL said otherwise. Moving the hook here is what makes "one reader" true of
|
|
22
|
+
* the component that most needs it.
|
|
23
|
+
*
|
|
24
|
+
* ## The list is the CALLER'S, and it is the real one
|
|
25
|
+
*
|
|
26
|
+
* `ids` is the tabs that exist right now, which is what makes an unknown id
|
|
27
|
+
* fall back instead of selecting a panel nothing renders. Manage Account is
|
|
28
|
+
* why that matters beyond typos: its Security section is absent for an
|
|
29
|
+
* SSO-governed account with no password, so `?tab=security` is a valid id on
|
|
30
|
+
* one account and a blank page on another.
|
|
31
|
+
*
|
|
32
|
+
* ## It keeps following the param
|
|
33
|
+
*
|
|
34
|
+
* Not just on mount. Back and forward are navigations between two states of
|
|
35
|
+
* the same page, and a docs link or an in-app link can change the param under
|
|
36
|
+
* a mounted page — read once, either leaves the old tab selected while the URL
|
|
37
|
+
* says otherwise.
|
|
38
|
+
*
|
|
39
|
+
* This cannot fight the reader's own clicks: a click writes the param, so the
|
|
40
|
+
* param and the state already agree by the time the effect looks.
|
|
41
|
+
*/
|
|
42
|
+
export interface UseTabParamOptions {
|
|
43
|
+
/** The tabs that exist, in order. The first is the default. */
|
|
44
|
+
ids: readonly string[];
|
|
45
|
+
/** Query key. `tab` everywhere today. */
|
|
46
|
+
param?: string;
|
|
47
|
+
/** Selected when the param is absent or names no tab. Defaults to `ids[0]`. */
|
|
48
|
+
fallback?: string;
|
|
49
|
+
/** Ran after a change — the pages log a `screen_view` here. */
|
|
50
|
+
onChange?: (id: string) => void;
|
|
51
|
+
}
|
|
52
|
+
export interface UseTabParamResult {
|
|
53
|
+
tab: string;
|
|
54
|
+
/** Drop-in for MUI `TabList`'s `onChange`. */
|
|
55
|
+
onTabChange: (event: unknown, value: string) => void;
|
|
56
|
+
}
|
|
57
|
+
export declare function useTabParam(options: UseTabParamOptions): UseTabParamResult;
|
|
58
|
+
export default useTabParam;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
18
|
+
import { useCallback, useEffect, useState } from "react";
|
|
19
|
+
export function useTabParam(options) {
|
|
20
|
+
var _ref, _ref1;
|
|
21
|
+
const { ids, param = 'tab', fallback, onChange } = options;
|
|
22
|
+
const router = useRouter();
|
|
23
|
+
const pathname = usePathname();
|
|
24
|
+
const searchParams = useSearchParams();
|
|
25
|
+
const requested = (_ref = searchParams == null ? void 0 : searchParams.get(param)) != null ? _ref : null;
|
|
26
|
+
// `''` and not `undefined` on an empty list: MUI's `TabContext` takes the
|
|
27
|
+
// selected id as its context value, and `undefined` there renders a rail
|
|
28
|
+
// with no selection and warns on every render. A hub whose tabs are built
|
|
29
|
+
// from entitlements can legitimately have none for one render.
|
|
30
|
+
const fallbackId = (_ref1 = fallback != null ? fallback : ids[0]) != null ? _ref1 : '';
|
|
31
|
+
/*
|
|
32
|
+
`ids.includes` and not a Set: these lists are single digits, the array is
|
|
33
|
+
rebuilt every render by callers that derive it, and a Set built per render
|
|
34
|
+
to search five strings costs more than the search.
|
|
35
|
+
*/ const resolved = requested && ids.includes(requested) ? requested : fallbackId;
|
|
36
|
+
const [tab, setTab] = useState(resolved);
|
|
37
|
+
// Follow the param when it changes — back/forward, or a link into another
|
|
38
|
+
// section of a page already open. `resolved` is already validated, so an id
|
|
39
|
+
// that has gone away (a section that stopped rendering) lands on the
|
|
40
|
+
// fallback rather than on nothing.
|
|
41
|
+
useEffect(()=>{
|
|
42
|
+
setTab((current)=>current === resolved ? current : resolved);
|
|
43
|
+
}, [
|
|
44
|
+
resolved
|
|
45
|
+
]);
|
|
46
|
+
const onTabChange = useCallback((_event, value)=>{
|
|
47
|
+
setTab(value);
|
|
48
|
+
// Shallow replace, no scroll: the section deep-links and survives
|
|
49
|
+
// back/forward without the page jumping to the top on every tab click.
|
|
50
|
+
const next = new URLSearchParams(searchParams == null ? void 0 : searchParams.toString());
|
|
51
|
+
next.set(param, value);
|
|
52
|
+
router.replace(`${pathname}?${next.toString()}`, {
|
|
53
|
+
scroll: false
|
|
54
|
+
});
|
|
55
|
+
onChange == null ? void 0 : onChange(value);
|
|
56
|
+
}, [
|
|
57
|
+
router,
|
|
58
|
+
pathname,
|
|
59
|
+
searchParams,
|
|
60
|
+
param,
|
|
61
|
+
onChange
|
|
62
|
+
]);
|
|
63
|
+
return {
|
|
64
|
+
tab,
|
|
65
|
+
onTabChange
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export default useTabParam;
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=use-tab-param.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/next/src/lib/hooks/use-tab-param.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport { useCallback, useEffect, useState } from 'react'\n\n/**\n * `?tab=` ↔ the selected vertical tab, in one place (AGL-2486, AGL-2501).\n *\n * Every surface with vertical tabs deep-links the same way, and it is this\n * hook that makes that true. Left to themselves, three pages produce three\n * different answers and one of them is wrong: validating the incoming id\n * against a HAND-MAINTAINED LIST silently opens the default tab for any id\n * added later; validating against two hardcoded ids is correct only until a\n * third arrives; and reading `window.location.search` once on mount ignores\n * every later navigation.\n *\n * ## Why this lives in the library rather than in the console\n *\n * The console is not the only surface with a vertical tab rail. `HubTabs` —\n * the rail itself — is shared, and every relocated feature plugin's console\n * page renders through it. A library cannot import from an app, so a hook\n * that lived in `apps/console/hooks` was one the rail could not use, and the\n * rail therefore grew the fourth answer: it read the parameter once into\n * `useState` and never looked again, so back, forward and an in-app link into\n * another section of an open page all left the rail on the old tab while the\n * URL said otherwise. Moving the hook here is what makes \"one reader\" true of\n * the component that most needs it.\n *\n * ## The list is the CALLER'S, and it is the real one\n *\n * `ids` is the tabs that exist right now, which is what makes an unknown id\n * fall back instead of selecting a panel nothing renders. Manage Account is\n * why that matters beyond typos: its Security section is absent for an\n * SSO-governed account with no password, so `?tab=security` is a valid id on\n * one account and a blank page on another.\n *\n * ## It keeps following the param\n *\n * Not just on mount. Back and forward are navigations between two states of\n * the same page, and a docs link or an in-app link can change the param under\n * a mounted page — read once, either leaves the old tab selected while the URL\n * says otherwise.\n *\n * This cannot fight the reader's own clicks: a click writes the param, so the\n * param and the state already agree by the time the effect looks.\n */\nexport interface UseTabParamOptions {\n /** The tabs that exist, in order. The first is the default. */\n ids: readonly string[]\n /** Query key. `tab` everywhere today. */\n param?: string\n /** Selected when the param is absent or names no tab. Defaults to `ids[0]`. */\n fallback?: string\n /** Ran after a change — the pages log a `screen_view` here. */\n onChange?: (id: string) => void\n}\n\nexport interface UseTabParamResult {\n tab: string\n /** Drop-in for MUI `TabList`'s `onChange`. */\n onTabChange: (event: unknown, value: string) => void\n}\n\nexport function useTabParam(options: UseTabParamOptions): UseTabParamResult {\n const { ids, param = 'tab', fallback, onChange } = options\n const router = useRouter()\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const requested = searchParams?.get(param) ?? null\n // `''` and not `undefined` on an empty list: MUI's `TabContext` takes the\n // selected id as its context value, and `undefined` there renders a rail\n // with no selection and warns on every render. A hub whose tabs are built\n // from entitlements can legitimately have none for one render.\n const fallbackId = fallback ?? ids[0] ?? ''\n /*\n `ids.includes` and not a Set: these lists are single digits, the array is\n rebuilt every render by callers that derive it, and a Set built per render\n to search five strings costs more than the search.\n */\n const resolved = requested && ids.includes(requested) ? requested : fallbackId\n const [tab, setTab] = useState(resolved)\n\n // Follow the param when it changes — back/forward, or a link into another\n // section of a page already open. `resolved` is already validated, so an id\n // that has gone away (a section that stopped rendering) lands on the\n // fallback rather than on nothing.\n useEffect(() => {\n setTab((current) => (current === resolved ? current : resolved))\n }, [resolved])\n\n\n const onTabChange = useCallback(\n (_event: unknown, value: string) => {\n setTab(value)\n // Shallow replace, no scroll: the section deep-links and survives\n // back/forward without the page jumping to the top on every tab click.\n const next = new URLSearchParams(searchParams?.toString())\n next.set(param, value)\n router.replace(`${pathname}?${next.toString()}`, { scroll: false })\n onChange?.(value)\n },\n [router, pathname, searchParams, param, onChange],\n )\n\n return { tab, onTabChange }\n}\n\nexport default useTabParam\n"],"names":["usePathname","useRouter","useSearchParams","useCallback","useEffect","useState","useTabParam","options","fallback","ids","param","onChange","router","pathname","searchParams","requested","get","fallbackId","resolved","includes","tab","setTab","current","onTabChange","_event","value","next","URLSearchParams","toString","set","replace","scroll"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAEA,SAASA,WAAW,EAAEC,SAAS,EAAEC,eAAe,QAAQ,kBAAiB;AACzE,SAASC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AA4DxD,OAAO,SAASC,YAAYC,OAA2B;cAUlCC;IATnB,MAAM,EAAEC,GAAG,EAAEC,QAAQ,KAAK,EAAEF,QAAQ,EAAEG,QAAQ,EAAE,GAAGJ;IACnD,MAAMK,SAASX;IACf,MAAMY,WAAWb;IACjB,MAAMc,eAAeZ;IACrB,MAAMa,oBAAYD,gCAAAA,aAAcE,GAAG,CAACN,yBAAU;IAC9C,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,+DAA+D;IAC/D,MAAMO,cAAaT,QAAAA,mBAAAA,WAAYC,GAAG,CAAC,EAAE,YAAlBD,QAAsB;IACzC;;;;EAIA,GACA,MAAMU,WAAWH,aAAaN,IAAIU,QAAQ,CAACJ,aAAaA,YAAYE;IACpE,MAAM,CAACG,KAAKC,OAAO,GAAGhB,SAASa;IAE/B,0EAA0E;IAC1E,4EAA4E;IAC5E,qEAAqE;IACrE,mCAAmC;IACnCd,UAAU;QACRiB,OAAO,CAACC,UAAaA,YAAYJ,WAAWI,UAAUJ;IACxD,GAAG;QAACA;KAAS;IAGb,MAAMK,cAAcpB,YAClB,CAACqB,QAAiBC;QAChBJ,OAAOI;QACP,kEAAkE;QAClE,uEAAuE;QACvE,MAAMC,OAAO,IAAIC,gBAAgBb,gCAAAA,aAAcc,QAAQ;QACvDF,KAAKG,GAAG,CAACnB,OAAOe;QAChBb,OAAOkB,OAAO,CAAC,GAAGjB,SAAS,CAAC,EAAEa,KAAKE,QAAQ,IAAI,EAAE;YAAEG,QAAQ;QAAM;QACjEpB,4BAAAA,SAAWc;IACb,GACA;QAACb;QAAQC;QAAUC;QAAcJ;QAAOC;KAAS;IAGnD,OAAO;QAAES;QAAKG;IAAY;AAC5B;AAEA,eAAejB,YAAW"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
export * from './components/hub-tabs';
|
|
18
|
+
export * from './components/image';
|
|
19
|
+
export * from './components/page-decorated';
|
|
20
|
+
export * from './contexts/next-page-title-provider';
|
package/src/lib/next.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ // This barrel serves App Router consumers only. The Pages Router `_app` /
|
|
17
|
+
// `_document` components that once lived beside these were deleted with
|
|
18
|
+
// `apps/www`, the last Pages Router app (AGL-1833): their `next/document`
|
|
19
|
+
// import cannot resolve in an App Router production build, so nothing here
|
|
20
|
+
// may ever re-export a module that touches it.
|
|
21
|
+
export * from "./components/hub-tabs.js";
|
|
22
|
+
export * from "./components/image.js";
|
|
23
|
+
export * from "./components/page-decorated.js";
|
|
24
|
+
export * from "./contexts/next-page-title-provider.js";
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=next.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/shared/ui/next/src/lib/next.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2023 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This barrel serves App Router consumers only. The Pages Router `_app` /\n// `_document` components that once lived beside these were deleted with\n// `apps/www`, the last Pages Router app (AGL-1833): their `next/document`\n// import cannot resolve in an App Router production build, so nothing here\n// may ever re-export a module that touches it.\nexport * from './components/hub-tabs'\nexport * from './components/image'\nexport * from './components/page-decorated'\n\nexport * from './contexts/next-page-title-provider'\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,0EAA0E;AAC1E,wEAAwE;AACxE,0EAA0E;AAC1E,2EAA2E;AAC3E,+CAA+C;AAC/C,cAAc,2BAAuB;AACrC,cAAc,wBAAoB;AAClC,cAAc,iCAA6B;AAE3C,cAAc,yCAAqC"}
|