ab-nextjs-core 0.1.4 โ 0.1.6
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 +20 -0
- package/README.md +48 -2
- package/ab-app-layout/index.tsx +81 -22
- package/ab-app-layout/styles.module.css +218 -1
- package/ab-aside-layout/index.tsx +76 -30
- package/ab-aside-layout/styles.module.css +54 -1
- package/ab-linear-progress/index.tsx +1 -1
- package/ab-main-layout/index.tsx +57 -23
- package/ab-main-layout/styles.module.css +43 -1
- package/ab-page-provider/index.tsx +144 -6
- package/ab-page-provider/scroll-registrar.tsx +71 -0
- package/ab-page-provider/slot.ts +51 -0
- package/ab-screen-layout/index.tsx +3 -3
- package/ab-screen-layout/styles.module.css +1 -1
- package/index.ts +1 -1
- package/package.json +4 -3
- package/server/ab-app-layout/index.tsx +56 -25
- package/server/ab-app-layout/styles.module.css +218 -1
- package/server/ab-aside-layout/index.tsx +62 -28
- package/server/ab-aside-layout/styles.module.css +43 -1
- package/server/ab-main-layout/index.tsx +59 -25
- package/server/ab-main-layout/styles.module.css +43 -1
- package/server/ab-screen-layout/index.tsx +3 -3
- package/server/ab-screen-layout/styles.module.css +1 -1
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
26
|
* @project: ab-nextjs-core
|
|
27
|
-
* @authors: Abraham Ukachi <
|
|
27
|
+
* @authors: Abraham Ukachi <abrahamukachi@gmail.com>
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
/*
|
|
@@ -37,6 +37,10 @@ import type { ReactElement, ReactNode } from 'react';
|
|
|
37
37
|
import clsx from 'clsx';
|
|
38
38
|
import AbLinearProgress from '../../ab-linear-progress';
|
|
39
39
|
import styles from './styles.module.css';
|
|
40
|
+
// ๐ซฅ G52: empty-slot helper (strings get a <span>, so the :has() collapse never hides text)
|
|
41
|
+
import { toAbSlot } from '../../ab-page-provider/slot';
|
|
42
|
+
// ๐ G55 (Joseph): client island that publishes this pane's scroller on useAbPage()
|
|
43
|
+
import AbScrollRegistrar from '../../ab-page-provider/scroll-registrar';
|
|
40
44
|
|
|
41
45
|
export interface AbMainLayoutProps {
|
|
42
46
|
orientation: 'vertical' | 'horizontal';
|
|
@@ -44,11 +48,22 @@ export interface AbMainLayoutProps {
|
|
|
44
48
|
hidden?: boolean;
|
|
45
49
|
pageName?: string;
|
|
46
50
|
screenName?: string;
|
|
51
|
+
/** ๐ 0.1.6 (G52): the main pane's header slot (data-ab-part="main-header"); 0px and not rendered when empty. */
|
|
52
|
+
mainHeader?: ReactNode;
|
|
53
|
+
/** ๐ 0.1.6 (G52): the main pane's bottom bar slot (data-ab-part="main-bar"); 0px and not rendered when empty. */
|
|
54
|
+
mainBar?: ReactNode;
|
|
55
|
+
/** @deprecated 0.1.6: use `mainHeader` (kept as an alias for one minor). */
|
|
47
56
|
header?: ReactNode;
|
|
48
57
|
content?: ReactNode;
|
|
58
|
+
/** @deprecated 0.1.6: use `mainBar` (kept as an alias for one minor). */
|
|
49
59
|
footer?: ReactNode;
|
|
50
60
|
children?: ReactNode;
|
|
51
61
|
menus?: ReactNode;
|
|
62
|
+
/** ๐ 0.1.6: element id (e.g. the skip-link target), no inner wrapper needed. */
|
|
63
|
+
id?: string;
|
|
64
|
+
/** @deprecated 0.1.6: `false` drops the legacy backdrop / menus / dialogs / toasts / progress / canvas layers.
|
|
65
|
+
* Empty header / bar slots are never rendered any more, so this only gates those layers (default `true`). */
|
|
66
|
+
chrome?: boolean;
|
|
52
67
|
}
|
|
53
68
|
|
|
54
69
|
/**
|
|
@@ -60,14 +75,22 @@ const AbMainLayout = ({
|
|
|
60
75
|
hidden,
|
|
61
76
|
pageName,
|
|
62
77
|
screenName,
|
|
63
|
-
|
|
78
|
+
mainHeader,
|
|
79
|
+
mainBar,
|
|
80
|
+
header, // ๐ฐ๏ธ deprecated alias
|
|
64
81
|
content,
|
|
65
|
-
footer,
|
|
82
|
+
footer, // ๐ฐ๏ธ deprecated alias
|
|
66
83
|
children,
|
|
67
84
|
menus,
|
|
85
|
+
id, // ๐
|
|
86
|
+
chrome = true, // ๐งฐ
|
|
68
87
|
}: AbMainLayoutProps): ReactElement => {
|
|
88
|
+
// ๐๐ G52: the new slot names win, the 0.1.5 names still work
|
|
89
|
+
const top = toAbSlot(mainHeader ?? header);
|
|
90
|
+
const bar = toAbSlot(mainBar ?? footer);
|
|
69
91
|
return (
|
|
70
92
|
<main
|
|
93
|
+
id={id} // ๐ 0.1.6
|
|
71
94
|
className={clsx(
|
|
72
95
|
['AbMainLayout', 'group-mainLayout', styles.abMainLayout, className],
|
|
73
96
|
{ 'has-screen-name': !!screenName },
|
|
@@ -78,9 +101,11 @@ const AbMainLayout = ({
|
|
|
78
101
|
data-ab-part="main"
|
|
79
102
|
data-screen-name={screenName}
|
|
80
103
|
>
|
|
81
|
-
|
|
82
|
-
{header
|
|
83
|
-
|
|
104
|
+
{top != null && ( // ๐ซฅ not rendered when empty (D37)
|
|
105
|
+
<header className={clsx(['HeaderWrapper', styles.abMainLayout__headerWrapper])} data-ab-part="main-header">
|
|
106
|
+
{top}
|
|
107
|
+
</header>
|
|
108
|
+
)}
|
|
84
109
|
|
|
85
110
|
<div
|
|
86
111
|
className={clsx([
|
|
@@ -88,31 +113,40 @@ const AbMainLayout = ({
|
|
|
88
113
|
styles.abMainLayout__contentWrapper,
|
|
89
114
|
orientation === 'horizontal' && 'md:!flex-row',
|
|
90
115
|
])}
|
|
116
|
+
data-ab-part="main-content" // ๐ G51/G55: THE scroller of this pane (useAbScrollSpy root)
|
|
91
117
|
data-orientation={orientation ? orientation.toString() : undefined}
|
|
92
118
|
>
|
|
119
|
+
{/* ๐ sets mainScrollElement (server components can't hold refs) */}
|
|
120
|
+
<AbScrollRegistrar pane="main" />
|
|
93
121
|
{content ?? children}
|
|
94
122
|
</div>
|
|
95
123
|
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<div className={clsx(['Menus', styles.abMainLayout__menus])} hidden>
|
|
102
|
-
{menus}
|
|
103
|
-
</div>
|
|
104
|
-
<div className={clsx(['Dialogs', styles.abMainLayout__dialogs])} hidden />
|
|
105
|
-
<div className={clsx(['Toasts', styles.abMainLayout__toasts])} hidden />
|
|
106
|
-
|
|
107
|
-
<AbLinearProgress
|
|
108
|
-
className="absolute top-0 left-0 right-0 z-50 !w-1/2 place-self-center !my-1 3xl:!my-2 !mx-auto !h-0.5 lg:!h-1 3xl:!h-1.5"
|
|
109
|
-
color="secondary"
|
|
110
|
-
hidden={true}
|
|
111
|
-
isIndeterminate={true}
|
|
112
|
-
progress={25}
|
|
113
|
-
/>
|
|
124
|
+
{bar != null && ( // ๐ซฅ not rendered when empty (D37)
|
|
125
|
+
<footer className={clsx(['FooterWrapper', styles.abMainLayout__footerWrapper])} data-ab-part="main-bar">
|
|
126
|
+
{bar}
|
|
127
|
+
</footer>
|
|
128
|
+
)}
|
|
114
129
|
|
|
115
|
-
|
|
130
|
+
{chrome && ( // ๐งฐ 0.1.6: legacy layers
|
|
131
|
+
<>
|
|
132
|
+
<div className={clsx(['Backdrop', styles.abMainLayout__backdrop])} hidden />
|
|
133
|
+
<div className={clsx(['Menus', styles.abMainLayout__menus])} hidden>
|
|
134
|
+
{menus}
|
|
135
|
+
</div>
|
|
136
|
+
<div className={clsx(['Dialogs', styles.abMainLayout__dialogs])} hidden />
|
|
137
|
+
<div className={clsx(['Toasts', styles.abMainLayout__toasts])} hidden />
|
|
138
|
+
|
|
139
|
+
<AbLinearProgress
|
|
140
|
+
className="absolute top-0 left-0 right-0 z-50 !w-1/2 place-self-center !my-1 3xl:!my-2 !mx-auto !h-0.5 lg:!h-1 3xl:!h-1.5"
|
|
141
|
+
color="secondary"
|
|
142
|
+
hidden={true}
|
|
143
|
+
isIndeterminate={true}
|
|
144
|
+
progress={25}
|
|
145
|
+
/>
|
|
146
|
+
|
|
147
|
+
<canvas className={clsx(['Canvas', styles.abMainLayout__canvas])} />
|
|
148
|
+
</>
|
|
149
|
+
)}
|
|
116
150
|
</main>
|
|
117
151
|
);
|
|
118
152
|
};
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* @name: AB Main Layout - Component Stylesheet (plain CSS, Tailwind-independent)
|
|
28
28
|
* @file: server/ab-main-layout/styles.module.css
|
|
29
29
|
* @type: CSS Module
|
|
30
|
-
* @authors: Abraham Ukachi <
|
|
30
|
+
* @authors: Abraham Ukachi <abrahamukachi@gmail.com>
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
/*
|
|
@@ -165,3 +165,45 @@
|
|
|
165
165
|
from { opacity: 0; }
|
|
166
166
|
to { opacity: 1; }
|
|
167
167
|
}
|
|
168
|
+
|
|
169
|
+
/* ================================================================== */
|
|
170
|
+
/* ๐ 0.1.6 layout rule (G51 + G52 + G54): a bounded box whose CONTENT */
|
|
171
|
+
/* scrolls on its own; main-header / main-bar collapse when empty */
|
|
172
|
+
/* ================================================================== */
|
|
173
|
+
/* ๐ซฅ D37: an empty slot is 0px (also outside panes: an empty wrapper had no size anyway) */
|
|
174
|
+
.abMainLayout__headerWrapper:empty,
|
|
175
|
+
.abMainLayout__footerWrapper:empty,
|
|
176
|
+
:global(.AbAppLayout[data-variant="panes"]) .abMainLayout__headerWrapper:not(:has(> *)),
|
|
177
|
+
:global(.AbAppLayout[data-variant="panes"]) .abMainLayout__footerWrapper:not(:has(> *)) {
|
|
178
|
+
display: none; /* ๐ 0px, and no footer gradient */
|
|
179
|
+
}
|
|
180
|
+
/* ๐งญ inside AbAppLayout variant="panes": a full-height column, only the content wrapper scrolls */
|
|
181
|
+
:global(.AbAppLayout[data-variant="panes"]) .abMainLayout {
|
|
182
|
+
height: 100%; /* ๐ the pane */
|
|
183
|
+
min-height: 0; /* ๐ซ */
|
|
184
|
+
max-height: none; /* ๐งน the legacy 100vh caps */
|
|
185
|
+
overflow: hidden; /* ๐ */
|
|
186
|
+
display: flex; /* ๐งฑ header ยท content ยท bar */
|
|
187
|
+
flex-direction: column;
|
|
188
|
+
margin: 0; /* ๐งน legacy 2xl margins */
|
|
189
|
+
}
|
|
190
|
+
:global(.AbAppLayout[data-variant="panes"]) .abMainLayout__headerWrapper,
|
|
191
|
+
:global(.AbAppLayout[data-variant="panes"]) .abMainLayout__footerWrapper {
|
|
192
|
+
flex: none; /* ๐ rows size to their content: no hard-coded heights */
|
|
193
|
+
pointer-events: auto; /* ๐ฑ๏ธ real chrome, not an overlay */
|
|
194
|
+
}
|
|
195
|
+
:global(.AbAppLayout[data-variant="panes"]) .abMainLayout__footerWrapper {
|
|
196
|
+
padding-bottom: env(safe-area-inset-bottom, 0px); /* ๐ฑ home indicator */
|
|
197
|
+
}
|
|
198
|
+
:global(.AbAppLayout[data-variant="panes"]) .abMainLayout__footerWrapper::before {
|
|
199
|
+
content: none; /* ๐งน no gradient over the scroller */
|
|
200
|
+
}
|
|
201
|
+
:global(.AbAppLayout[data-variant="panes"]) .abMainLayout__contentWrapper {
|
|
202
|
+
flex: 1 1 0; /* โ๏ธ the rest */
|
|
203
|
+
height: auto; /* ๐ */
|
|
204
|
+
min-height: 0; /* ๐ซ lets it scroll */
|
|
205
|
+
overflow: hidden auto; /* ๐ THE scroller of this pane */
|
|
206
|
+
overscroll-behavior: contain; /* ๐ซ never chains to the page (G54) */
|
|
207
|
+
scrollbar-gutter: stable; /* ๐ no layout jump */
|
|
208
|
+
justify-content: flex-start; /* โฌ๏ธ */
|
|
209
|
+
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
26
|
* @project: ab-nextjs-core
|
|
27
|
-
* @authors: Abraham Ukachi <
|
|
27
|
+
* @authors: Abraham Ukachi <abrahamukachi@gmail.com>
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
/*
|
|
@@ -72,7 +72,7 @@ const AbScreenLayout = ({
|
|
|
72
72
|
data-screen-name={screenName}
|
|
73
73
|
data-orientation={orientation ?? ''}
|
|
74
74
|
>
|
|
75
|
-
<header className={clsx(['HeaderWrapper', styles.abScreenLayout__headerWrapper])}>
|
|
75
|
+
<header className={clsx(['HeaderWrapper', styles.abScreenLayout__headerWrapper])} data-ab-part="header">
|
|
76
76
|
{header}
|
|
77
77
|
</header>
|
|
78
78
|
|
|
@@ -80,7 +80,7 @@ const AbScreenLayout = ({
|
|
|
80
80
|
{content ?? children}
|
|
81
81
|
</div>
|
|
82
82
|
|
|
83
|
-
<footer className={clsx(['FooterWrapper', styles.abScreenLayout__footerWrapper])}>
|
|
83
|
+
<footer className={clsx(['FooterWrapper', styles.abScreenLayout__footerWrapper])} data-ab-part="footer">
|
|
84
84
|
{footer}
|
|
85
85
|
</footer>
|
|
86
86
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* @name: AB Screen Layout - Component Stylesheet (plain CSS, Tailwind-independent)
|
|
28
28
|
* @file: server/ab-screen-layout/styles.module.css
|
|
29
29
|
* @type: CSS Module
|
|
30
|
-
* @authors: Abraham Ukachi <
|
|
30
|
+
* @authors: Abraham Ukachi <abrahamukachi@gmail.com>
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
/*
|