Compare commits

..

5 commits

Author SHA1 Message Date
thislight
62aaaeee9a
Profile: check container query supports
All checks were successful
/ depoly (push) Successful in 1m25s
2024-11-23 16:13:42 +08:00
thislight
66d0bc8d84
Profile: support wide page 2024-11-23 16:10:48 +08:00
thislight
bb3ba32dc5
Masonry: remove built-in column number condition 2024-11-23 13:53:10 +08:00
thislight
fbbac36b4a
StackedRouter: minor changes 2024-11-23 13:51:53 +08:00
thislight
487de9237b
Masonry: use MutationObserver instead
- originally tricks with the reactive system
2024-11-23 13:00:36 +08:00
6 changed files with 377 additions and 332 deletions

View file

@ -5,12 +5,7 @@
@supports (grid-template-rows: masonry) { @supports (grid-template-rows: masonry) {
.NativeMasonry { .NativeMasonry {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(44px, min-content)); grid-template-columns: repeat(auto-fit, minmax(33%, min-content));
grid-template-rows: masonry; grid-template-rows: masonry;
&:has(> :last-child:nth-child(2n)) {
grid-template-columns: repeat(2, minmax(auto, min-content));
} }
} }
}

View file

@ -5,10 +5,8 @@ import {
type Ref, type Ref,
createRenderEffect, createRenderEffect,
onCleanup, onCleanup,
children,
createEffect, createEffect,
createSignal, createSignal,
onMount,
} from "solid-js"; } from "solid-js";
import { Dynamic, type DynamicProps } from "solid-js/web"; import { Dynamic, type DynamicProps } from "solid-js/web";
import MasonryLayout from "masonry-layout"; import MasonryLayout from "masonry-layout";
@ -20,7 +18,6 @@ type MasonryContainer =
| Component<{ | Component<{
ref?: Ref<Element>; ref?: Ref<Element>;
class?: string; class?: string;
children?: JSX.Element;
}>; }>;
type ElementOf<T extends MasonryContainer> = type ElementOf<T extends MasonryContainer> =
@ -37,7 +34,10 @@ function forwardRef<T>(value: T, ref?: Ref<T>) {
(ref as (value: T) => void)(value); (ref as (value: T) => void)(value);
} }
function createMasonry(element: Element, options: () => MasonryLayout.Options) { function createCompatMasonry(
element: Element,
options: () => MasonryLayout.Options,
) {
const layout = new MasonryLayout(element, { const layout = new MasonryLayout(element, {
initLayout: false, initLayout: false,
}); });
@ -46,11 +46,21 @@ function createMasonry(element: Element, options: () => MasonryLayout.Options) {
const size = createElementSize(element); const size = createElementSize(element);
const treeMutObx = new MutationObserver(() => {
layout.reloadItems?.();
});
onCleanup(() => treeMutObx.disconnect());
createRenderEffect(() => { createRenderEffect(() => {
const opts = options(); const opts = options();
layout.option?.(opts); layout.option?.(opts);
}); });
createRenderEffect(() => {
treeMutObx.observe(element, { childList: true });
});
createRenderEffect(() => { createRenderEffect(() => {
const width = size.width; // only tracking width const width = size.width; // only tracking width
layout.layout?.(); layout.layout?.();
@ -61,8 +71,6 @@ function createMasonry(element: Element, options: () => MasonryLayout.Options) {
layout.layout?.(); layout.layout?.();
}); });
} }
return layout;
} }
const supportsCSSMasonryLayout = /* @__PURE__ */ CSS.supports( const supportsCSSMasonryLayout = /* @__PURE__ */ CSS.supports(
@ -85,9 +93,7 @@ if (import.meta.env.VITE_PLATFROM_MASONRY_ALWAYS_COMPAT) {
function MasonryCompat<T extends MasonryContainer>( function MasonryCompat<T extends MasonryContainer>(
oprops: DynamicProps<T> & { class?: string }, oprops: DynamicProps<T> & { class?: string },
) { ) {
const [props, rest] = splitProps(oprops, ["ref", "children", "class"]); const [props, rest] = splitProps(oprops, ["ref", "class"]);
const childrenComponents = children(() => props.children);
return ( return (
<Dynamic <Dynamic
@ -96,7 +102,7 @@ function MasonryCompat<T extends MasonryContainer>(
const [columnGap, setColumnGap] = createSignal<number>(); const [columnGap, setColumnGap] = createSignal<number>();
const layout = createMasonry(element, () => { createCompatMasonry(element, () => {
return { return {
gutter: columnGap(), gutter: columnGap(),
}; };
@ -115,18 +121,9 @@ function MasonryCompat<T extends MasonryContainer>(
setColumnGap(Number(colGap.slice(0, colGap.length - 2))); setColumnGap(Number(colGap.slice(0, colGap.length - 2)));
} }
}); });
createRenderEffect(() => {
childrenComponents(); // just tracks
setTimeout(() => {
layout.reloadItems?.();
layout.layout?.();
}, 0);
});
}} }}
class={`Masonry CompatMasonry ${props.class || ""}`} class={`Masonry CompatMasonry ${props.class || ""}`}
{...rest} {...rest}
children={childrenComponents}
/> />
); );
} }
@ -147,6 +144,13 @@ function MasonryNative<T extends MasonryContainer>(
* and fallback to masonry-layout if not supported. The children * and fallback to masonry-layout if not supported. The children
* must have specified width and height. * must have specified width and height.
* *
* Testing native behaviour:
* - Firefox: in `about:config`, search for `layout.css.grid-template-masonry-value.enabled`
*
* Class `NativeMasonry` will be added to the element if it's under the
* css masonry layout, otherwise it's `CompatMasonry`. `Masonry` is always
* added.
*
* **Children Changes** As the children changed, reflow will be triggered, * **Children Changes** As the children changed, reflow will be triggered,
* and there is might be a blink (or transition) for user. If it's not your * and there is might be a blink (or transition) for user. If it's not your
* intention, don't remove/add the direct children. Instead wraps them under * intention, don't remove/add the direct children. Instead wraps them under

View file

@ -1,10 +1,10 @@
.StackedPage { .StackedPage {
container: StackedPage / size; contain: strict;
display: contents; container: StackedPage / inline-size;
max-width: 100vw; width: 100vw;
max-width: 100dvw; width: 100dvw;
height: 100vh;
contain: layout; height: 100dvh;
} }
dialog.StackedPage { dialog.StackedPage {

View file

@ -115,6 +115,46 @@
} }
} }
@supports (container-type: inline-size) {
@container StackedPage (inline-size >=960px) {
.Profile {
display: grid;
grid-template-columns: auto 560px;
grid-template-rows: min-content 1fr;
height: 100cqh;
>.topbar {
grid-column: 1 / 3;
grid-row: 1 /2;
.MuiToolbar-root {
padding-right: calc(560px + 24px);
}
}
>.details {
height: 100%;
display: flex;
flex-flow: column nowrap;
>.intro {
flex-grow: 1;
}
}
>.recent-toots {
overflow-y: auto;
margin-top: calc(-1 * var(--scaffold-topbar-height));
z-index: calc(var(--tutu-zidx-nav, 1) + 1);
>.toot-list-toolbar {
top: 0;
}
}
}
}
}
.Profile__page-title { .Profile__page-title {
flex-grow: 1; flex-grow: 1;
white-space: nowrap; white-space: nowrap;

View file

@ -237,6 +237,7 @@ const Profile: Component = () => {
} }
class="Profile" class="Profile"
> >
<div class="details">
<Menu <Menu
id={optMenuId} id={optMenuId}
open={menuOpen()} open={menuOpen()}
@ -424,7 +425,9 @@ const Profile: Component = () => {
aria-label="Display name" aria-label="Display name"
></Body2> ></Body2>
</div> </div>
<span aria-label="Complete username" class="username">{fullUsername()}</span> <span aria-label="Complete username" class="username">
{fullUsername()}
</span>
</div> </div>
<div role="presentation"> <div role="presentation">
<Switch> <Switch>
@ -493,7 +496,9 @@ const Profile: Component = () => {
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
<div class="recent-toots">
<div class="toot-list-toolbar"> <div class="toot-list-toolbar">
<TootFilterButton <TootFilterButton
options={{ options={{
@ -546,6 +551,7 @@ const Profile: Component = () => {
</IconButton> </IconButton>
</div> </div>
</Show> </Show>
</div>
</Scaffold> </Scaffold>
); );
}; };