{children}
@@ -73,6 +74,7 @@ export default function MobileZone({
backgroundColor={zone?.[s]?.[a]?.background ?? "unset"}
layoutConstraint={layoutConstraint}
built={built}
+ isMobile={isMobile}
renderWidget={renderWidget}
onWidgetAreaSelect={onWidgetAreaSelect}
/>
diff --git a/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/Zone.tsx b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/Zone.tsx
index 25b2a4cc68..413482bae0 100644
--- a/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/Zone.tsx
+++ b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/Zone.tsx
@@ -2,6 +2,7 @@ import { ReactNode } from "react";
import { GridSection } from "react-align";
import Area, { WidgetAreaType } from "./Area";
+import { WAS_SECTIONS, WAS_AREAS } from "./constants";
import type { WidgetZone, WidgetLayoutConstraint, WidgetProps } from "./types";
export type { WidgetAreaType };
@@ -13,29 +14,28 @@ export type Props = {
zoneName: "inner" | "outer";
invisibleWidgetIDs?: string[];
layoutConstraint?: { [w: string]: WidgetLayoutConstraint };
+ isMobile?: boolean;
built?: boolean;
renderWidget?: (props: WidgetProps) => ReactNode;
onWidgetAreaSelect?: (widgetArea?: WidgetAreaType) => void;
};
-const sections = ["left", "center", "right"] as const;
-const areas = ["top", "middle", "bottom"] as const;
-
export default function Zone({
selectedWidgetArea,
zone,
zoneName,
layoutConstraint,
built,
+ isMobile,
children,
renderWidget,
onWidgetAreaSelect,
}: Props) {
return (
<>
- {sections.map(s => (
+ {WAS_SECTIONS.map(s => (
- {areas.map(a =>
+ {WAS_AREAS.map(a =>
s === "center" && children && a === "middle" ? (
{children}
@@ -55,6 +55,7 @@ export default function Zone({
centered={zone?.[s]?.[a]?.centered}
layoutConstraint={layoutConstraint}
built={built}
+ isMobile={isMobile}
renderWidget={renderWidget}
onWidgetAreaSelect={onWidgetAreaSelect}
/>
diff --git a/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/constants.ts b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/constants.ts
new file mode 100644
index 0000000000..0eb045605c
--- /dev/null
+++ b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/constants.ts
@@ -0,0 +1,5 @@
+import { type WidgetZone, type WidgetSection, type WidgetAlignSystem } from ".";
+
+export const WAS_SECTIONS = ["left", "center", "right"] as (keyof WidgetZone)[];
+export const WAS_AREAS = ["top", "middle", "bottom"] as (keyof WidgetSection)[];
+export const WAS_ZONES = ["outer", "inner"] as (keyof WidgetAlignSystem)[];
diff --git a/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/index.tsx b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/index.tsx
index 7793f3afeb..7a96c16da0 100644
--- a/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/index.tsx
+++ b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/index.tsx
@@ -96,6 +96,7 @@ const WidgetAlignSystem: React.FC
= ({
invisibleWidgetIDs={invisibleWidgetIDs}
theme={theme}
built={built}
+ isMobile={isMobile}
renderWidget={renderWidget}
onWidgetAreaSelect={onWidgetAreaSelect}>
{(!isMobile || hasInner) && (
@@ -106,6 +107,7 @@ const WidgetAlignSystem: React.FC = ({
zone={alignSystem?.inner}
layoutConstraint={layoutConstraint}
built={built}
+ isMobile={isMobile}
renderWidget={renderWidget}
onWidgetAreaSelect={onWidgetAreaSelect}
/>
diff --git a/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/types.ts b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/types.ts
index 972f32618c..66cc9dfcdf 100644
--- a/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/types.ts
+++ b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/types.ts
@@ -58,4 +58,5 @@ export type WidgetProps = {
extended?: boolean;
editing: boolean;
onExtend?: (id: string, extended: boolean | undefined) => void;
+ onVisibilityChange?: (widgetId: string, visible: boolean) => void;
};
diff --git a/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/utils.ts b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/utils.ts
index e73aee74ab..5f17917129 100644
--- a/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/utils.ts
+++ b/web/src/beta/lib/core/Crust/Widgets/WidgetAlignSystem/utils.ts
@@ -1,16 +1,29 @@
-import { WidgetZone } from "./types";
+import { isBuiltinWidget } from "../Widget/builtin";
-const sections = ["left", "center", "right"] as const;
-const areas = ["top", "middle", "bottom"] as const;
+import { WAS_SECTIONS, WAS_AREAS } from "./constants";
+import { InternalWidget, WidgetZone } from "./types";
export const filterSections = (
zone?: WidgetZone,
invisibleWidgetIDs?: string[],
- cb?: (s: (typeof sections)[number]) => void,
+ cb?: (s: (typeof WAS_SECTIONS)[number]) => void,
) => {
- return sections.filter(
+ return WAS_SECTIONS.filter(
s =>
- areas.filter(a => zone?.[s]?.[a]?.widgets?.find(w => !invisibleWidgetIDs?.includes(w.id)))
+ WAS_AREAS.filter(a => zone?.[s]?.[a]?.widgets?.some(w => !invisibleWidgetIDs?.includes(w.id)))
.length || cb?.(s),
);
};
+
+export const isInvisibleBuiltin = (widget: InternalWidget, isMobile?: boolean) => {
+ const defaultVisible = widget.property?.default?.visible;
+ return (
+ isBuiltinWidget(`${widget.pluginId}/${widget.extensionId}`) &&
+ !(
+ !defaultVisible ||
+ defaultVisible === "always" ||
+ (defaultVisible === "desktop" && !isMobile) ||
+ (defaultVisible === "mobile" && !!isMobile)
+ )
+ );
+};
diff --git a/web/src/beta/lib/core/Crust/Widgets/index.tsx b/web/src/beta/lib/core/Crust/Widgets/index.tsx
index 7e20e0f4d8..4f2bbee760 100644
--- a/web/src/beta/lib/core/Crust/Widgets/index.tsx
+++ b/web/src/beta/lib/core/Crust/Widgets/index.tsx
@@ -60,7 +60,7 @@ export type WidgetProps = {
layout?: WidgetLayout;
onExtend?: (id: string, extended: boolean | undefined) => void;
onWidgetMove?: (widgetId: string, options: WidgetLocationOptions) => void;
- onVisibilityChange: (widgetId: string, v: boolean) => void;
+ onVisibilityChange?: (widgetId: string, visible: boolean) => void;
};
export default function Widgets({
@@ -80,9 +80,10 @@ export default function Widgets({
onWidgetLayoutUpdate,
onWidgetAreaSelect,
}: Props): JSX.Element | null {
- const { overriddenAlignSystem, moveWidget, onVisibilityChange, invisibleWidgetIDs } =
+ const { overriddenAlignSystem, moveWidget, invisibleWidgetIDs, onPluginWidgetVisibilityChange } =
useWidgetAlignSystem({
alignSystem,
+ isMobile,
});
const renderWidgetInternal = useCallback(
@@ -106,10 +107,9 @@ export default function Widgets({
layout,
onExtend,
onWidgetMove: moveWidget,
- onVisibilityChange,
+ onVisibilityChange: onPluginWidgetVisibilityChange,
})
}
- onVisibilityChange={onVisibilityChange}
onExtend={onExtend}
/>
),
@@ -120,9 +120,9 @@ export default function Widgets({
isBuilt,
isMobile,
context,
- onVisibilityChange,
renderWidget,
moveWidget,
+ onPluginWidgetVisibilityChange,
],
);
diff --git a/web/src/beta/lib/core/Crust/Widgets/useWidgetAlignSystem.ts b/web/src/beta/lib/core/Crust/Widgets/useWidgetAlignSystem.ts
index 82618a16a1..f8c77e4d6f 100644
--- a/web/src/beta/lib/core/Crust/Widgets/useWidgetAlignSystem.ts
+++ b/web/src/beta/lib/core/Crust/Widgets/useWidgetAlignSystem.ts
@@ -1,15 +1,24 @@
import { useEffect, useState, useCallback } from "react";
-import type {
- WidgetAlignSystem,
- InternalWidget,
- WidgetLocationOptions,
- WidgetArea,
- WidgetSection,
- WidgetZone,
+import { WAS_SECTIONS, WAS_AREAS, WAS_ZONES } from "./WidgetAlignSystem/constants";
+import { isInvisibleBuiltin } from "./WidgetAlignSystem/utils";
+
+import {
+ type WidgetAlignSystem,
+ type InternalWidget,
+ type WidgetLocationOptions,
+ type WidgetArea,
+ type WidgetSection,
+ type WidgetZone,
} from ".";
-export default ({ alignSystem }: { alignSystem: WidgetAlignSystem | undefined }) => {
+export default ({
+ alignSystem,
+ isMobile,
+}: {
+ alignSystem: WidgetAlignSystem | undefined;
+ isMobile?: boolean;
+}) => {
const [overriddenAlignSystem, setOverrideAlignSystem] = useState(
alignSystem,
);
@@ -17,9 +26,25 @@ export default ({ alignSystem }: { alignSystem: WidgetAlignSystem | undefined })
// NOTE: This is invisible list of widget.
// The reason why we use invisible list is prevent initializing cost.
const [invisibleWidgetIDs, setInvisibleWidgetIDs] = useState([]);
+ const [invisiblePluginWidgetIDs, setInvisiblePluginWidgetIDs] = useState([]);
- const onVisibilityChange = useCallback((widgetId: string, v: boolean) => {
- setInvisibleWidgetIDs(a => {
+ useEffect(() => {
+ const invisibleBuiltinWidgetIDs: string[] = [];
+ WAS_ZONES.forEach(zone => {
+ WAS_SECTIONS.forEach(section => {
+ WAS_AREAS.forEach(area => {
+ overriddenAlignSystem?.[zone]?.[section]?.[area]?.widgets?.forEach(w => {
+ if (isInvisibleBuiltin(w, isMobile)) invisibleBuiltinWidgetIDs.push(w.id);
+ });
+ });
+ });
+ });
+
+ setInvisibleWidgetIDs([...invisibleBuiltinWidgetIDs, ...invisiblePluginWidgetIDs]);
+ }, [isMobile, overriddenAlignSystem, invisiblePluginWidgetIDs]);
+
+ const onPluginWidgetVisibilityChange = useCallback((widgetId: string, v: boolean) => {
+ setInvisiblePluginWidgetIDs(a => {
if (!a.includes(widgetId) && !v) {
return [...a, widgetId];
}
@@ -32,9 +57,9 @@ export default ({ alignSystem }: { alignSystem: WidgetAlignSystem | undefined })
const moveWidget = useCallback((widgetId: string, options: WidgetLocationOptions) => {
if (
- !["outer", "inner"].includes(options.zone) ||
- !["left", "center", "right"].includes(options.section) ||
- !["top", "middle", "bottom"].includes(options.area) ||
+ !WAS_ZONES.includes(options.zone) ||
+ !WAS_SECTIONS.includes(options.section) ||
+ !WAS_AREAS.includes(options.area) ||
(options.section === "center" && options.area === "middle")
)
return;
@@ -132,8 +157,8 @@ export default ({ alignSystem }: { alignSystem: WidgetAlignSystem | undefined })
return {
overriddenAlignSystem,
- moveWidget,
invisibleWidgetIDs,
- onVisibilityChange,
+ moveWidget,
+ onPluginWidgetVisibilityChange,
};
};
diff --git a/web/src/classic/components/molecules/Visualizer/Widget/SplashScreen/index.tsx b/web/src/classic/components/molecules/Visualizer/Widget/SplashScreen/index.tsx
index 2ec3cc0392..0b2143d8b1 100644
--- a/web/src/classic/components/molecules/Visualizer/Widget/SplashScreen/index.tsx
+++ b/web/src/classic/components/molecules/Visualizer/Widget/SplashScreen/index.tsx
@@ -44,7 +44,6 @@ const SplashScreen = ({ widget, inEditor }: Props): JSX.Element | null => {
overlayTitle: title,
} = property?.overlay ?? {};
const camera = (property as Property | undefined)?.camera?.filter(c => !!c.cameraPosition);
-
const [cameraSequence, setCameraSequence] = useState(0);
const [delayedCameraSequence, setDelayedCameraSequence] = useState(-1);
const currentCamera = camera?.[cameraSequence];
diff --git a/web/src/classic/components/molecules/Visualizer/Widget/index.tsx b/web/src/classic/components/molecules/Visualizer/Widget/index.tsx
index 8e01a332e8..4c99a2a265 100644
--- a/web/src/classic/components/molecules/Visualizer/Widget/index.tsx
+++ b/web/src/classic/components/molecules/Visualizer/Widget/index.tsx
@@ -28,9 +28,10 @@ export type Props = {
editing?: boolean;
viewport?: Viewport;
onExtend?: (id: string, extended: boolean | undefined) => void;
+ onVisibilityChange?: (widgetId: string, visible: boolean) => void;
} & PluginCommonProps;
-export type ComponentProps = Omit, "widget" | "viewport"> & {
+export type ComponentProps = Omit, "widget"> & {
widget: RawWidget;
};
@@ -105,7 +106,14 @@ export default function WidgetComponent({
return viewport ? (
Builtin ? (
-
+
) : (
- {widgets?.map((widget, i) => {
- const constraint =
- widget.pluginId && widget.extensionId
- ? layoutConstraint?.[`${widget.pluginId}/${widget.extensionId}`]
- : undefined;
- const extended = overriddenExtended?.[widget.id];
- const extendable2 =
- (section === "center" && constraint?.extendable?.horizontally) ||
- (area === "middle" && constraint?.extendable?.vertically);
- return (
-
- {({ editing }) => (
-
- )}
-
- );
- })}
+ {widgets
+ ?.filter(widget => !isInvisibleBuiltin(widget, viewport?.isMobile))
+ ?.map((widget, i) => {
+ const constraint =
+ widget.pluginId && widget.extensionId
+ ? layoutConstraint?.[`${widget.pluginId}/${widget.extensionId}`]
+ : undefined;
+ const extended = overriddenExtended?.[widget.id];
+ const extendable2 =
+ (section === "center" && constraint?.extendable?.horizontally) ||
+ (area === "middle" && constraint?.extendable?.vertically);
+ return (
+
+ {({ editing }) => (
+
+ )}
+
+ );
+ })}
) : null;
}
diff --git a/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/MobileZone.tsx b/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/MobileZone.tsx
index 2766e03033..5b7e9b4d54 100644
--- a/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/MobileZone.tsx
+++ b/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/MobileZone.tsx
@@ -10,7 +10,9 @@ import { Viewport } from "../hooks";
import type { CommonProps as PluginCommonProps } from "../Plugin";
import Area from "./Area";
+import { WAS_AREAS } from "./constants";
import type { WidgetZone, WidgetLayoutConstraint } from "./hooks";
+import { filterSections } from "./utils";
export type Props = {
children?: ReactNode;
@@ -23,12 +25,10 @@ export type Props = {
isBuilt?: boolean;
sceneProperty?: any;
viewport?: Viewport;
+ invisibleWidgetIDs?: string[];
onWidgetAlignAreaSelect?: (widgetArea?: WidgetAreaState) => void;
} & PluginCommonProps;
-const sections = ["left", "center", "right"] as const;
-const areas = ["top", "middle", "bottom"] as const;
-
export default function MobileZone({
isMobileZone,
selectedWidgetArea,
@@ -40,16 +40,14 @@ export default function MobileZone({
pluginBaseUrl,
isEditable,
isBuilt,
+ invisibleWidgetIDs,
onWidgetAlignAreaSelect,
children,
...props
}: Props) {
const filteredSections = useMemo(() => {
- return sections.filter(
- s =>
- areas.filter(a => zone?.[s]?.[a]?.widgets?.length).length || (s === "center" && children),
- );
- }, [zone, children]);
+ return filterSections(zone, invisibleWidgetIDs, s => s === "center" && children);
+ }, [zone, children, invisibleWidgetIDs]);
const initialPos = useMemo(() => (filteredSections.length === 3 ? 1 : 0), [filteredSections]);
@@ -65,7 +63,7 @@ export default function MobileZone({
1}>
{filteredSections.map(s => (
- {areas.map(a =>
+ {WAS_AREAS.map(a =>
s === "center" && children && a === "middle" ? (
{children}
diff --git a/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/Zone.tsx b/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/Zone.tsx
index c3fec6eb74..268b1789be 100644
--- a/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/Zone.tsx
+++ b/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/Zone.tsx
@@ -7,6 +7,7 @@ import { Viewport } from "../hooks";
import type { CommonProps as PluginCommonProps } from "../Plugin";
import Area from "./Area";
+import { WAS_SECTIONS, WAS_AREAS } from "./constants";
import type { WidgetZone, WidgetLayoutConstraint } from "./hooks";
export type Props = {
@@ -24,9 +25,6 @@ export type Props = {
onWidgetAlignAreaSelect?: (widgetAreaState?: WidgetAreaState) => void;
} & PluginCommonProps;
-const sections = ["left", "center", "right"] as const;
-const areas = ["top", "middle", "bottom"] as const;
-
export default function Zone({
isMobileZone,
selectedWidgetArea,
@@ -44,9 +42,9 @@ export default function Zone({
}: Props) {
return (
<>
- {sections.map(s => (
+ {WAS_SECTIONS.map(s => (
- {areas.map(a =>
+ {WAS_AREAS.map(a =>
s === "center" && children && a === "middle" ? (
{children}
diff --git a/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/constants.ts b/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/constants.ts
new file mode 100644
index 0000000000..0eb045605c
--- /dev/null
+++ b/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/constants.ts
@@ -0,0 +1,5 @@
+import { type WidgetZone, type WidgetSection, type WidgetAlignSystem } from ".";
+
+export const WAS_SECTIONS = ["left", "center", "right"] as (keyof WidgetZone)[];
+export const WAS_AREAS = ["top", "middle", "bottom"] as (keyof WidgetSection)[];
+export const WAS_ZONES = ["outer", "inner"] as (keyof WidgetAlignSystem)[];
diff --git a/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/index.tsx b/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/index.tsx
index 60f9b7e5f9..8fb59f6b41 100644
--- a/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/index.tsx
+++ b/web/src/classic/components/molecules/Visualizer/WidgetAlignSystem/index.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useMemo } from "react";
import { GridWrapper } from "react-align";
import { WidgetAreaState } from "@reearth/classic/components/organisms/EarthEditor/PropertyPane/hooks";
@@ -15,6 +15,7 @@ import type {
WidgetLayoutConstraint,
} from "./hooks";
import MobileZone from "./MobileZone";
+import { filterSections } from "./utils";
import ZoneComponent from "./Zone";
export type {
@@ -38,6 +39,7 @@ export type Props = {
isBuilt?: boolean;
viewport?: Viewport;
sceneProperty?: any;
+ invisibleWidgetIDs?: string[];
onWidgetUpdate?: (
id: string,
update: {
@@ -49,6 +51,7 @@ export type Props = {
onWidgetAlignSystemUpdate?: (location: Location, align: Alignment) => void;
overrideSceneProperty?: (pluginId: string, property: any) => void;
onWidgetAlignAreaSelect?: (widgetArea?: WidgetAreaState) => void;
+ onVisibilityChange?: (widgetId: string, visible: boolean) => void;
} & PluginCommonProps;
const WidgetAlignSystem: React.FC
= ({
@@ -61,6 +64,7 @@ const WidgetAlignSystem: React.FC = ({
isEditable,
isBuilt,
layoutConstraint,
+ invisibleWidgetIDs,
onWidgetUpdate,
onWidgetAlignSystemUpdate,
onWidgetAlignAreaSelect,
@@ -71,6 +75,13 @@ const WidgetAlignSystem: React.FC = ({
onWidgetAlignSystemUpdate,
});
+ const hasInner = useMemo(() => {
+ if (!alignSystem?.inner) {
+ return;
+ }
+ return !!filterSections(alignSystem?.inner, invisibleWidgetIDs).length;
+ }, [alignSystem, invisibleWidgetIDs]);
+
return (
= ({
layoutConstraint={layoutConstraint}
onWidgetAlignAreaSelect={onWidgetAlignAreaSelect}
{...props}>
- {alignSystem?.inner && (
+ {hasInner && (
void,
+) => {
+ return WAS_SECTIONS.filter(
+ s =>
+ WAS_AREAS.filter(a => zone?.[s]?.[a]?.widgets?.some(w => !invisibleWidgetIDs?.includes(w.id)))
+ .length || cb?.(s),
+ );
+};
+
+export const isInvisibleBuiltin = (widget: Widget, isMobile?: boolean) => {
+ const defaultVisible = widget.property?.default?.visible;
+ return (
+ isBuiltinWidget(`${widget.pluginId}/${widget.extensionId}`) &&
+ !(
+ !defaultVisible ||
+ defaultVisible === "always" ||
+ (defaultVisible === "desktop" && !isMobile) ||
+ (defaultVisible === "mobile" && !!isMobile)
+ )
+ );
+};
diff --git a/web/src/classic/components/molecules/Visualizer/hooks.ts b/web/src/classic/components/molecules/Visualizer/hooks.ts
index 654a497791..7c7292be05 100644
--- a/web/src/classic/components/molecules/Visualizer/hooks.ts
+++ b/web/src/classic/components/molecules/Visualizer/hooks.ts
@@ -218,9 +218,11 @@ export default ({
}
}, [isPublished, enableGA, trackingId]);
- const { overriddenAlignSystem, moveWidget } = useWidgetAlignSystem({
- alignSystem,
- });
+ const { overriddenAlignSystem, moveWidget, invisibleWidgetIDs, onPluginWidgetVisibilityChange } =
+ useWidgetAlignSystem({
+ alignSystem,
+ isMobile: viewport.isMobile,
+ });
const pluginInstances = usePluginInstances({
alignSystem,
@@ -301,6 +303,8 @@ export default ({
shownPluginPopupInfo,
viewport,
overriddenAlignSystem,
+ invisibleWidgetIDs,
+ onPluginWidgetVisibilityChange,
onPluginModalShow,
onPluginPopupShow,
isLayerHidden,
diff --git a/web/src/classic/components/molecules/Visualizer/index.tsx b/web/src/classic/components/molecules/Visualizer/index.tsx
index ccbb11cf2c..107fb01f9d 100644
--- a/web/src/classic/components/molecules/Visualizer/index.tsx
+++ b/web/src/classic/components/molecules/Visualizer/index.tsx
@@ -137,6 +137,8 @@ export default function Visualizer({
shownPluginPopupInfo,
overriddenAlignSystem,
viewport,
+ invisibleWidgetIDs,
+ onPluginWidgetVisibilityChange,
onPluginModalShow,
onPluginPopupShow,
isLayerHidden,
@@ -200,6 +202,8 @@ export default function Visualizer({
pluginBaseUrl={pluginBaseUrl}
layoutConstraint={widgets.layoutConstraint}
onWidgetAlignAreaSelect={onWidgetAlignAreaSelect}
+ invisibleWidgetIDs={invisibleWidgetIDs}
+ onVisibilityChange={onPluginWidgetVisibilityChange}
/>
)}
{
+export default ({
+ alignSystem,
+ isMobile,
+}: {
+ alignSystem: WidgetAlignSystem | undefined;
+ isMobile?: boolean;
+}) => {
const [overriddenAlignSystem, setOverrideAlignSystem] = useState(
alignSystem,
);
const moveWidget = useCallback((widgetId: string, options: WidgetLocationOptions) => {
if (
- !["outer", "inner"].includes(options.zone) ||
- !["left", "center", "right"].includes(options.section) ||
- !["top", "middle", "bottom"].includes(options.area) ||
+ !WAS_ZONES.includes(options.zone) ||
+ !WAS_SECTIONS.includes(options.section) ||
+ !WAS_AREAS.includes(options.area) ||
(options.section === "center" && options.area === "middle")
)
return;
@@ -111,8 +119,42 @@ export default ({ alignSystem }: { alignSystem: WidgetAlignSystem | undefined })
setOverrideAlignSystem(alignSystem);
}, [alignSystem]);
+ // NOTE: This is invisible list of widget.
+ // The reason why we use invisible list is prevent initializing cost.
+ const [invisibleWidgetIDs, setInvisibleWidgetIDs] = useState([]);
+ const [invisiblePluginWidgetIDs, setInvisiblePluginWidgetIDs] = useState([]);
+
+ useEffect(() => {
+ const invisibleBuiltinWidgetIDs: string[] = [];
+ WAS_ZONES.forEach(zone => {
+ WAS_SECTIONS.forEach(section => {
+ WAS_AREAS.forEach(area => {
+ overriddenAlignSystem?.[zone]?.[section]?.[area]?.widgets?.forEach(w => {
+ if (isInvisibleBuiltin(w, isMobile)) invisibleBuiltinWidgetIDs.push(w.id);
+ });
+ });
+ });
+ });
+
+ setInvisibleWidgetIDs([...invisibleBuiltinWidgetIDs, ...invisiblePluginWidgetIDs]);
+ }, [isMobile, overriddenAlignSystem, invisiblePluginWidgetIDs]);
+
+ const onPluginWidgetVisibilityChange = useCallback((widgetId: string, v: boolean) => {
+ setInvisiblePluginWidgetIDs(a => {
+ if (!a.includes(widgetId) && !v) {
+ return [...a, widgetId];
+ }
+ if (a.includes(widgetId) && v) {
+ return a.filter(i => i !== widgetId);
+ }
+ return a;
+ });
+ }, []);
+
return {
overriddenAlignSystem,
+ invisibleWidgetIDs,
moveWidget,
+ onPluginWidgetVisibilityChange,
};
};
diff --git a/web/src/classic/core/Crust/Widgets/Widget/Button/MenuButton.tsx b/web/src/classic/core/Crust/Widgets/Widget/Button/MenuButton.tsx
index c989cc4cb7..77ba99f89a 100644
--- a/web/src/classic/core/Crust/Widgets/Widget/Button/MenuButton.tsx
+++ b/web/src/classic/core/Crust/Widgets/Widget/Button/MenuButton.tsx
@@ -8,7 +8,6 @@ import { metricsSizes } from "@reearth/classic/theme";
import { styled, mask } from "@reearth/services/theme";
import type { Camera, FlyToDestination, Theme } from "../types";
-import { Visible } from "../useVisible";
export type Button = {
id: string;
@@ -20,7 +19,7 @@ export type Button = {
buttonColor?: string;
buttonBgcolor?: string;
buttonCamera?: Camera;
- visible: Visible;
+ visible?: "always" | "desktop" | "mobile";
};
export type MenuItem = {
diff --git a/web/src/classic/core/Crust/Widgets/Widget/Button/index.tsx b/web/src/classic/core/Crust/Widgets/Widget/Button/index.tsx
index 55a93d0e24..751a1c0fc6 100644
--- a/web/src/classic/core/Crust/Widgets/Widget/Button/index.tsx
+++ b/web/src/classic/core/Crust/Widgets/Widget/Button/index.tsx
@@ -1,7 +1,6 @@
import { styled } from "@reearth/services/theme";
import type { ComponentProps as WidgetProps } from "..";
-import { useVisible } from "../useVisible";
import MenuButton, { Button as ButtonType, MenuItem as MenuItemType } from "./MenuButton";
@@ -13,22 +12,10 @@ export type Property = {
menu?: MenuItem[];
};
-const Menu = ({
- widget,
- theme,
- isMobile,
- onVisibilityChange,
- context: { onFlyTo } = {},
-}: Props): JSX.Element | null => {
+const Menu = ({ widget, theme, context: { onFlyTo } = {} }: Props): JSX.Element | null => {
const { default: button, menu: menuItems } = widget.property ?? {};
- const visible = useVisible({
- widgetId: widget.id,
- visible: widget.property?.default?.visible,
- isMobile,
- onVisibilityChange,
- });
- return visible ? (
+ return (
- ) : null;
+ );
};
const Wrapper = styled.div`
diff --git a/web/src/classic/core/Crust/Widgets/Widget/Navigator/hooks.ts b/web/src/classic/core/Crust/Widgets/Widget/Navigator/hooks.ts
index dfa84f756c..2be3eb78c3 100644
--- a/web/src/classic/core/Crust/Widgets/Widget/Navigator/hooks.ts
+++ b/web/src/classic/core/Crust/Widgets/Widget/Navigator/hooks.ts
@@ -1,21 +1,17 @@
import { useState, useEffect, useCallback, useRef } from "react";
import type { Camera, FlyToDestination, Widget } from "../types";
-import { useVisible } from "../useVisible";
import { degreeToRadian, radianToDegree } from "./NavigatorPresenter";
export default function ({
camera,
initialCamera,
- widget,
- isMobile,
onZoomIn,
onZoomOut,
onCameraOrbit,
onCameraRotateRight,
onFlyTo,
- onVisibilityChange,
}: {
camera?: Camera;
initialCamera?: Camera;
@@ -26,18 +22,11 @@ export default function ({
onCameraOrbit?: (orbit: number) => void;
onCameraRotateRight?: (radian: number) => void;
onFlyTo?: (target: string | FlyToDestination, options?: { duration?: number }) => void;
- onVisibilityChange?: (id: string, visible: boolean) => void;
}) {
const [degree, setDegree] = useState(0);
const [isHelpOpened, setIsHelpOpened] = useState(false);
const orbitRadianRef = useRef(0);
const isMovingOrbit = useRef(false);
- const visible = useVisible({
- widgetId: widget.id,
- visible: widget.property?.default?.visible,
- isMobile,
- onVisibilityChange,
- });
const handleOnRotate = useCallback(
(deg: number) => {
@@ -101,7 +90,6 @@ export default function ({
return {
degree,
isHelpOpened,
- visible,
events: {
onRotate: handleOnRotate,
onStartOrbit: handleOnStartOrbit,
diff --git a/web/src/classic/core/Crust/Widgets/Widget/Navigator/index.tsx b/web/src/classic/core/Crust/Widgets/Widget/Navigator/index.tsx
index 6807f2724d..2a79d4993a 100644
--- a/web/src/classic/core/Crust/Widgets/Widget/Navigator/index.tsx
+++ b/web/src/classic/core/Crust/Widgets/Widget/Navigator/index.tsx
@@ -1,23 +1,15 @@
import type { ComponentProps as WidgetProps } from "..";
-import { Visible } from "../useVisible";
import useHooks from "./hooks";
import NavigatorPresenter from "./NavigatorPresenter";
-export type Props = WidgetProps;
-
-export type Property = {
- default: {
- visible: Visible;
- };
-};
+export type Props = WidgetProps;
const Navigator = ({
theme,
editing,
widget,
isMobile,
- onVisibilityChange,
context: {
camera,
initialCamera,
@@ -28,7 +20,7 @@ const Navigator = ({
onZoomOut,
} = {},
}: Props): JSX.Element | null => {
- const { degree, visible, events } = useHooks({
+ const { degree, events } = useHooks({
camera,
initialCamera,
widget,
@@ -38,12 +30,9 @@ const Navigator = ({
onFlyTo,
onZoomIn,
onZoomOut,
- onVisibilityChange,
});
- return visible ? (
-
- ) : null;
+ return ;
};
export default Navigator;
diff --git a/web/src/classic/core/Crust/Widgets/Widget/SplashScreen/index.tsx b/web/src/classic/core/Crust/Widgets/Widget/SplashScreen/index.tsx
index e5340c9b6f..512c35a1b3 100644
--- a/web/src/classic/core/Crust/Widgets/Widget/SplashScreen/index.tsx
+++ b/web/src/classic/core/Crust/Widgets/Widget/SplashScreen/index.tsx
@@ -6,7 +6,6 @@ import { styled } from "@reearth/services/theme";
import type { ComponentProps as WidgetProps } from "..";
import type { Camera } from "../types";
-import { useVisible, Visible } from "../useVisible";
export type Props = WidgetProps;
@@ -21,7 +20,7 @@ export type Property = {
overlayImageW?: number;
overlayImageH?: number;
overlayTitle?: string;
- visible?: Visible;
+ visible?: "always" | "desktop" | "mobile";
};
camera?: {
cameraPosition?: Camera;
@@ -33,8 +32,6 @@ export type Property = {
const SplashScreen = ({
widget,
inEditor,
- isMobile,
- onVisibilityChange,
context: { onFlyTo } = {},
}: Props): JSX.Element | null => {
const { property } = widget ?? {};
@@ -50,12 +47,6 @@ const SplashScreen = ({
overlayTitle: title,
} = property?.overlay ?? {};
const camera = property?.camera?.filter(c => !!c.cameraPosition);
- const visible = useVisible({
- widgetId: widget.id,
- visible: widget.property?.overlay.visible,
- isMobile,
- onVisibilityChange,
- });
const [cameraSequence, setCameraSequence] = useState(0);
const [delayedCameraSequence, setDelayedCameraSequence] = useState(-1);
@@ -106,7 +97,7 @@ const SplashScreen = ({
return () => clearTimeout(t);
}, [delayedCurrentCamera, inEditor]);
- return !visible || state === "unmounted" ? null : (
+ return state === "unmounted" ? null : (
diff --git a/web/src/classic/core/Crust/Widgets/Widget/Storytelling/index.tsx b/web/src/classic/core/Crust/Widgets/Widget/Storytelling/index.tsx
index 8b2eab8b74..df91b94ce3 100644
--- a/web/src/classic/core/Crust/Widgets/Widget/Storytelling/index.tsx
+++ b/web/src/classic/core/Crust/Widgets/Widget/Storytelling/index.tsx
@@ -21,6 +21,7 @@ export type Property = {
range?: number;
camera?: Camera;
autoStart?: boolean;
+ visible?: "always" | "desktop" | "mobile";
};
stories?: StoryType[];
};
diff --git a/web/src/classic/core/Crust/Widgets/Widget/Timeline/hooks.ts b/web/src/classic/core/Crust/Widgets/Widget/Timeline/hooks.ts
index 2723f76c1c..cdc5150d0c 100644
--- a/web/src/classic/core/Crust/Widgets/Widget/Timeline/hooks.ts
+++ b/web/src/classic/core/Crust/Widgets/Widget/Timeline/hooks.ts
@@ -4,7 +4,6 @@ import type { TimeEventHandler } from "@reearth/classic/components/atoms/Timelin
import { TickEvent, TickEventCallback } from "@reearth/classic/core/Map";
import type { Clock, Widget } from "../types";
-import { useVisible } from "../useVisible";
const MAX_RANGE = 86400000; // a day
const getOrNewDate = (d?: Date) => d ?? new Date();
@@ -31,7 +30,6 @@ export const useTimeline = ({
onTick,
removeTickEventListener,
onExtend,
- onVisibilityChange,
}: {
widget: Widget;
clock?: Clock;
@@ -44,14 +42,7 @@ export const useTimeline = ({
onTick?: TickEvent;
removeTickEventListener?: TickEvent;
onExtend?: (id: string, extended: boolean | undefined) => void;
- onVisibilityChange?: (id: string, v: boolean) => void;
}) => {
- const visible = useVisible({
- widgetId: widget.id,
- visible: widget.property?.default?.visible,
- isMobile,
- onVisibilityChange,
- });
const widgetId = widget.id;
const [range, setRange] = useState(() =>
makeRange(clock?.start?.getTime(), clock?.stop?.getTime()),
@@ -227,7 +218,6 @@ export const useTimeline = ({
range,
isOpened,
currentTime,
- visible,
events: {
onOpen: handleOnOpen,
onClose: handleOnClose,
diff --git a/web/src/classic/core/Crust/Widgets/Widget/Timeline/index.tsx b/web/src/classic/core/Crust/Widgets/Widget/Timeline/index.tsx
index 5f72cb7208..4337d04dfe 100644
--- a/web/src/classic/core/Crust/Widgets/Widget/Timeline/index.tsx
+++ b/web/src/classic/core/Crust/Widgets/Widget/Timeline/index.tsx
@@ -2,24 +2,16 @@ import TimelineUI from "@reearth/classic/components/atoms/Timeline";
import { styled } from "@reearth/services/theme";
import type { ComponentProps as WidgetProps } from "..";
-import { Visible } from "../useVisible";
import { useTimeline } from "./hooks";
-export type Props = WidgetProps;
-
-export type Property = {
- default: {
- visible: Visible;
- };
-};
+export type Props = WidgetProps;
const Timeline = ({
widget,
theme,
isMobile,
onExtend,
- onVisibilityChange,
context: {
clock,
overriddenClock,
@@ -31,7 +23,7 @@ const Timeline = ({
removeTickEventListener,
} = {},
}: Props): JSX.Element | null => {
- const { isOpened, currentTime, range, speed, events, visible } = useTimeline({
+ const { isOpened, currentTime, range, speed, events } = useTimeline({
widget,
clock,
overriddenClock,
@@ -43,10 +35,9 @@ const Timeline = ({
onTick,
removeTickEventListener,
onExtend,
- onVisibilityChange,
});
- return visible ? (
+ return (
- ) : null;
+ );
};
const Widget = styled.div<{
diff --git a/web/src/classic/core/Crust/Widgets/Widget/index.tsx b/web/src/classic/core/Crust/Widgets/Widget/index.tsx
index 3867a3f7a9..48fbe3ef57 100644
--- a/web/src/classic/core/Crust/Widgets/Widget/index.tsx
+++ b/web/src/classic/core/Crust/Widgets/Widget/index.tsx
@@ -30,7 +30,6 @@ export type Props = {
isMobile?: boolean;
context?: Context;
onExtend?: (id: string, extended: boolean | undefined) => void;
- onVisibilityChange?: (id: string, visible: boolean) => void;
renderWidget?: (w: Widget) => ReactNode;
};
diff --git a/web/src/classic/core/Crust/Widgets/Widget/useVisible.ts b/web/src/classic/core/Crust/Widgets/Widget/useVisible.ts
deleted file mode 100644
index e6ae3e7555..0000000000
--- a/web/src/classic/core/Crust/Widgets/Widget/useVisible.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useEffect, useMemo } from "react";
-
-export type Visible = "always" | "desktop" | "mobile";
-
-export const useVisible = ({
- widgetId,
- visible: defaultVisible,
- isMobile,
- onVisibilityChange,
-}: {
- widgetId: string | undefined;
- visible: Visible | undefined;
- isMobile: boolean | undefined;
- onVisibilityChange: ((id: string, v: boolean) => void) | undefined;
-}) => {
- const visible = useMemo(
- () =>
- !defaultVisible ||
- defaultVisible === "always" ||
- (defaultVisible === "desktop" && !isMobile) ||
- (defaultVisible === "mobile" && !!isMobile),
- [defaultVisible, isMobile],
- );
-
- useEffect(() => {
- if (widgetId) {
- onVisibilityChange?.(widgetId, visible);
- }
- }, [widgetId, visible, onVisibilityChange]);
-
- return visible;
-};
diff --git a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/Area.tsx b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/Area.tsx
index 6e132c4add..5f4e431f24 100644
--- a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/Area.tsx
+++ b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/Area.tsx
@@ -14,6 +14,7 @@ import type {
WidgetProps,
InternalWidget,
} from "./types";
+import { isInvisibleBuiltin } from "./utils";
export type WidgetAreaType = {
zone: "inner" | "outer";
@@ -39,6 +40,7 @@ type Props = {
centered?: boolean;
built?: boolean;
widgets?: InternalWidget[];
+ isMobile?: boolean;
onWidgetAreaSelect?: (widgetArea?: WidgetAreaType) => void;
// note that layoutConstraint will be always undefined in published pages
layoutConstraint?: { [w in string]: WidgetLayoutConstraint };
@@ -58,6 +60,7 @@ export default function Area({
built,
widgets,
layoutConstraint,
+ isMobile,
renderWidget,
onWidgetAreaSelect,
}: Props) {
@@ -136,36 +139,38 @@ export default function Area({
alignItems: centered ? "center" : "unset",
}}
iconColor={area === "middle" ? "#4770FF" : "#E95518"}>
- {widgets?.map((widget, i) => {
- const constraint =
- widget.pluginId && widget.extensionId
- ? layoutConstraint?.[`${widget.pluginId}/${widget.extensionId}`]
- : undefined;
- const extended = overriddenExtended?.[widget.id];
- const extendable2 =
- (section === "center" && constraint?.extendable?.horizontally) ||
- (area === "middle" && constraint?.extendable?.vertically);
- return (
-
- {({ editing }) =>
- renderWidget?.({
- widget,
- layout,
- extended,
- editing,
- onExtend: handleExtend,
- })
- }
-
- );
- })}
+ {widgets
+ ?.filter(widget => !isInvisibleBuiltin(widget, isMobile))
+ ?.map((widget, i) => {
+ const constraint =
+ widget.pluginId && widget.extensionId
+ ? layoutConstraint?.[`${widget.pluginId}/${widget.extensionId}`]
+ : undefined;
+ const extended = overriddenExtended?.[widget.id];
+ const extendable2 =
+ (section === "center" && constraint?.extendable?.horizontally) ||
+ (area === "middle" && constraint?.extendable?.vertically);
+ return (
+
+ {({ editing }) =>
+ renderWidget?.({
+ widget,
+ layout,
+ extended,
+ editing,
+ onExtend: handleExtend,
+ })
+ }
+
+ );
+ })}
) : null;
}
diff --git a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/MobileZone.tsx b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/MobileZone.tsx
index f8eed3288e..acc7b2d941 100644
--- a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/MobileZone.tsx
+++ b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/MobileZone.tsx
@@ -6,6 +6,7 @@ import Slide from "@reearth/classic/components/atoms/Slide";
import { styled } from "@reearth/services/theme";
import Area, { WidgetAreaType } from "./Area";
+import { WAS_AREAS } from "./constants";
import type { WidgetZone, WidgetLayoutConstraint, Theme, WidgetProps } from "./types";
import { filterSections } from "./utils";
@@ -18,12 +19,11 @@ export type Props = {
built?: boolean;
layoutConstraint?: { [w: string]: WidgetLayoutConstraint };
invisibleWidgetIDs?: string[];
+ isMobile?: boolean;
renderWidget?: (props: WidgetProps) => ReactNode;
onWidgetAreaSelect?: (widgetArea?: WidgetAreaType) => void;
};
-const areas = ["top", "middle", "bottom"] as const;
-
export default function MobileZone({
selectedWidgetArea,
zone,
@@ -33,6 +33,7 @@ export default function MobileZone({
built,
children,
invisibleWidgetIDs,
+ isMobile,
renderWidget,
onWidgetAreaSelect,
}: Props) {
@@ -53,7 +54,7 @@ export default function MobileZone({
1}>
{filteredSections.map(s => (
- {areas.map(a =>
+ {WAS_AREAS.map(a =>
s === "center" && children && a === "middle" ? (
{children}
@@ -73,6 +74,7 @@ export default function MobileZone({
backgroundColor={zone?.[s]?.[a]?.background ?? "unset"}
layoutConstraint={layoutConstraint}
built={built}
+ isMobile={isMobile}
renderWidget={renderWidget}
onWidgetAreaSelect={onWidgetAreaSelect}
/>
diff --git a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/Zone.tsx b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/Zone.tsx
index 25b2a4cc68..f66f4e6b02 100644
--- a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/Zone.tsx
+++ b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/Zone.tsx
@@ -2,6 +2,7 @@ import { ReactNode } from "react";
import { GridSection } from "react-align";
import Area, { WidgetAreaType } from "./Area";
+import { WAS_SECTIONS, WAS_AREAS } from "./constants";
import type { WidgetZone, WidgetLayoutConstraint, WidgetProps } from "./types";
export type { WidgetAreaType };
@@ -14,28 +15,27 @@ export type Props = {
invisibleWidgetIDs?: string[];
layoutConstraint?: { [w: string]: WidgetLayoutConstraint };
built?: boolean;
+ isMobile?: boolean;
renderWidget?: (props: WidgetProps) => ReactNode;
onWidgetAreaSelect?: (widgetArea?: WidgetAreaType) => void;
};
-const sections = ["left", "center", "right"] as const;
-const areas = ["top", "middle", "bottom"] as const;
-
export default function Zone({
selectedWidgetArea,
zone,
zoneName,
layoutConstraint,
built,
+ isMobile,
children,
renderWidget,
onWidgetAreaSelect,
}: Props) {
return (
<>
- {sections.map(s => (
+ {WAS_SECTIONS.map(s => (
- {areas.map(a =>
+ {WAS_AREAS.map(a =>
s === "center" && children && a === "middle" ? (
{children}
@@ -55,6 +55,7 @@ export default function Zone({
centered={zone?.[s]?.[a]?.centered}
layoutConstraint={layoutConstraint}
built={built}
+ isMobile={isMobile}
renderWidget={renderWidget}
onWidgetAreaSelect={onWidgetAreaSelect}
/>
diff --git a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/constants.ts b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/constants.ts
new file mode 100644
index 0000000000..0eb045605c
--- /dev/null
+++ b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/constants.ts
@@ -0,0 +1,5 @@
+import { type WidgetZone, type WidgetSection, type WidgetAlignSystem } from ".";
+
+export const WAS_SECTIONS = ["left", "center", "right"] as (keyof WidgetZone)[];
+export const WAS_AREAS = ["top", "middle", "bottom"] as (keyof WidgetSection)[];
+export const WAS_ZONES = ["outer", "inner"] as (keyof WidgetAlignSystem)[];
diff --git a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/index.tsx b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/index.tsx
index 1c59ac40f8..648ee861ac 100644
--- a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/index.tsx
+++ b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/index.tsx
@@ -96,6 +96,7 @@ const WidgetAlignSystem: React.FC
= ({
invisibleWidgetIDs={invisibleWidgetIDs}
theme={theme}
built={built}
+ isMobile={isMobile}
renderWidget={renderWidget}
onWidgetAreaSelect={onWidgetAreaSelect}>
{(!isMobile || hasInner) && (
@@ -106,6 +107,7 @@ const WidgetAlignSystem: React.FC = ({
zone={alignSystem?.inner}
layoutConstraint={layoutConstraint}
built={built}
+ isMobile={isMobile}
renderWidget={renderWidget}
onWidgetAreaSelect={onWidgetAreaSelect}
/>
diff --git a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/types.ts b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/types.ts
index 972f32618c..76710f374a 100644
--- a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/types.ts
+++ b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/types.ts
@@ -58,4 +58,5 @@ export type WidgetProps = {
extended?: boolean;
editing: boolean;
onExtend?: (id: string, extended: boolean | undefined) => void;
+ onVisibilityChange?: (id: string, visible: boolean) => void;
};
diff --git a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/utils.ts b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/utils.ts
index e73aee74ab..2fb2049104 100644
--- a/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/utils.ts
+++ b/web/src/classic/core/Crust/Widgets/WidgetAlignSystem/utils.ts
@@ -1,16 +1,29 @@
-import { WidgetZone } from "./types";
+import { isBuiltinWidget } from "../Widget/builtin";
-const sections = ["left", "center", "right"] as const;
-const areas = ["top", "middle", "bottom"] as const;
+import { WAS_SECTIONS, WAS_AREAS } from "./constants";
+import { InternalWidget, WidgetZone } from "./types";
export const filterSections = (
zone?: WidgetZone,
invisibleWidgetIDs?: string[],
- cb?: (s: (typeof sections)[number]) => void,
+ cb?: (s: (typeof WAS_SECTIONS)[number]) => void,
) => {
- return sections.filter(
+ return WAS_SECTIONS.filter(
s =>
- areas.filter(a => zone?.[s]?.[a]?.widgets?.find(w => !invisibleWidgetIDs?.includes(w.id)))
- .length || cb?.(s),
+ WAS_AREAS.filter(a => zone?.[s]?.[a]?.widgets?.some(w => !invisibleWidgetIDs?.includes(w.id)))
+ .length > 0 || cb?.(s),
+ );
+};
+
+export const isInvisibleBuiltin = (widget: InternalWidget, isMobile?: boolean) => {
+ const defaultVisible = widget.property?.default?.visible;
+ return (
+ isBuiltinWidget(`${widget.pluginId}/${widget.extensionId}`) &&
+ !(
+ !defaultVisible ||
+ defaultVisible === "always" ||
+ (defaultVisible === "desktop" && !isMobile) ||
+ (defaultVisible === "mobile" && !!isMobile)
+ )
);
};
diff --git a/web/src/classic/core/Crust/Widgets/index.tsx b/web/src/classic/core/Crust/Widgets/index.tsx
index 7e20e0f4d8..4f2bbee760 100644
--- a/web/src/classic/core/Crust/Widgets/index.tsx
+++ b/web/src/classic/core/Crust/Widgets/index.tsx
@@ -60,7 +60,7 @@ export type WidgetProps = {
layout?: WidgetLayout;
onExtend?: (id: string, extended: boolean | undefined) => void;
onWidgetMove?: (widgetId: string, options: WidgetLocationOptions) => void;
- onVisibilityChange: (widgetId: string, v: boolean) => void;
+ onVisibilityChange?: (widgetId: string, visible: boolean) => void;
};
export default function Widgets({
@@ -80,9 +80,10 @@ export default function Widgets({
onWidgetLayoutUpdate,
onWidgetAreaSelect,
}: Props): JSX.Element | null {
- const { overriddenAlignSystem, moveWidget, onVisibilityChange, invisibleWidgetIDs } =
+ const { overriddenAlignSystem, moveWidget, invisibleWidgetIDs, onPluginWidgetVisibilityChange } =
useWidgetAlignSystem({
alignSystem,
+ isMobile,
});
const renderWidgetInternal = useCallback(
@@ -106,10 +107,9 @@ export default function Widgets({
layout,
onExtend,
onWidgetMove: moveWidget,
- onVisibilityChange,
+ onVisibilityChange: onPluginWidgetVisibilityChange,
})
}
- onVisibilityChange={onVisibilityChange}
onExtend={onExtend}
/>
),
@@ -120,9 +120,9 @@ export default function Widgets({
isBuilt,
isMobile,
context,
- onVisibilityChange,
renderWidget,
moveWidget,
+ onPluginWidgetVisibilityChange,
],
);
diff --git a/web/src/classic/core/Crust/Widgets/useWidgetAlignSystem.ts b/web/src/classic/core/Crust/Widgets/useWidgetAlignSystem.ts
index 82618a16a1..5719d39f43 100644
--- a/web/src/classic/core/Crust/Widgets/useWidgetAlignSystem.ts
+++ b/web/src/classic/core/Crust/Widgets/useWidgetAlignSystem.ts
@@ -1,15 +1,24 @@
import { useEffect, useState, useCallback } from "react";
-import type {
- WidgetAlignSystem,
- InternalWidget,
- WidgetLocationOptions,
- WidgetArea,
- WidgetSection,
- WidgetZone,
+import { WAS_SECTIONS, WAS_AREAS, WAS_ZONES } from "./WidgetAlignSystem/constants";
+import { isInvisibleBuiltin } from "./WidgetAlignSystem/utils";
+
+import {
+ type WidgetAlignSystem,
+ type InternalWidget,
+ type WidgetLocationOptions,
+ type WidgetArea,
+ type WidgetSection,
+ type WidgetZone,
} from ".";
-export default ({ alignSystem }: { alignSystem: WidgetAlignSystem | undefined }) => {
+export default ({
+ alignSystem,
+ isMobile,
+}: {
+ alignSystem: WidgetAlignSystem | undefined;
+ isMobile?: boolean;
+}) => {
const [overriddenAlignSystem, setOverrideAlignSystem] = useState(
alignSystem,
);
@@ -17,9 +26,25 @@ export default ({ alignSystem }: { alignSystem: WidgetAlignSystem | undefined })
// NOTE: This is invisible list of widget.
// The reason why we use invisible list is prevent initializing cost.
const [invisibleWidgetIDs, setInvisibleWidgetIDs] = useState([]);
+ const [invisiblePluginWidgetIDs, setInvisiblePluginWidgetIDs] = useState([]);
+
+ useEffect(() => {
+ const invisibleBuiltinWidgetIDs: string[] = [];
+ WAS_ZONES.forEach(zone => {
+ WAS_SECTIONS.forEach(section => {
+ WAS_AREAS.forEach(area => {
+ overriddenAlignSystem?.[zone]?.[section]?.[area]?.widgets?.forEach(w => {
+ if (isInvisibleBuiltin(w, isMobile)) invisibleBuiltinWidgetIDs.push(w.id);
+ });
+ });
+ });
+ });
+
+ setInvisibleWidgetIDs([...invisibleBuiltinWidgetIDs, ...invisiblePluginWidgetIDs]);
+ }, [isMobile, overriddenAlignSystem, invisiblePluginWidgetIDs]);
- const onVisibilityChange = useCallback((widgetId: string, v: boolean) => {
- setInvisibleWidgetIDs(a => {
+ const onPluginWidgetVisibilityChange = useCallback((widgetId: string, v: boolean) => {
+ setInvisiblePluginWidgetIDs(a => {
if (!a.includes(widgetId) && !v) {
return [...a, widgetId];
}
@@ -32,9 +57,9 @@ export default ({ alignSystem }: { alignSystem: WidgetAlignSystem | undefined })
const moveWidget = useCallback((widgetId: string, options: WidgetLocationOptions) => {
if (
- !["outer", "inner"].includes(options.zone) ||
- !["left", "center", "right"].includes(options.section) ||
- !["top", "middle", "bottom"].includes(options.area) ||
+ !WAS_ZONES.includes(options.zone) ||
+ !WAS_SECTIONS.includes(options.section) ||
+ !WAS_AREAS.includes(options.area) ||
(options.section === "center" && options.area === "middle")
)
return;
@@ -134,6 +159,6 @@ export default ({ alignSystem }: { alignSystem: WidgetAlignSystem | undefined })
overriddenAlignSystem,
moveWidget,
invisibleWidgetIDs,
- onVisibilityChange,
+ onPluginWidgetVisibilityChange,
};
};