Skip to content

Commit 736bb59

Browse files
authored
Merge pull request #138 from aeagle/feature/ss-rendering
Experimental full SSR rendering functionality +semver: feature
2 parents a4edfee + 86bf0c9 commit 736bb59

12 files changed

+127
-34
lines changed

GitVersion.yml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
major-version-bump-message: '\+semver:\s?(breaking|major)'
2+
minor-version-bump-message: '\+semver:\s?(feature|minor)'
3+
patch-version-bump-message: '\+semver:\s?(fix|patch)'
4+
commit-message-incrementing: Enabled

nextjs-demo/pages/index.tsx

+14-4
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,28 @@ import * as Space from "react-spaces";
44
export default function Home() {
55
return (
66
<>
7+
<Space.SSR />
78
<Head>
89
<title>Create Next App</title>
910
<meta name="description" content="Generated by create next app" />
1011
<meta name="viewport" content="width=device-width, initial-scale=1" />
1112
<link rel="icon" href="/favicon.ico" />
1213
</Head>
13-
<Space.ViewPort id="viewport">
14-
<Space.LeftResizable id="left" size="25%" centerContent={Space.CenterType.HorizontalVertical} style={{ backgroundColor: "white" }}>
14+
<Space.ViewPort>
15+
<Space.LeftResizable size="25%" centerContent={Space.CenterType.HorizontalVertical} style={{ backgroundColor: "white" }}>
1516
<span style={{ color: "black" }}>Hello world</span>
1617
</Space.LeftResizable>
17-
<Space.Fill id="fill" centerContent={Space.CenterType.HorizontalVertical}>
18-
Hello world
18+
<Space.RightResizable size="25%" centerContent={Space.CenterType.HorizontalVertical} style={{ backgroundColor: "white" }}>
19+
<span style={{ color: "black" }}>Hello world</span>
20+
</Space.RightResizable>
21+
<Space.Fill>
22+
<Space.TopResizable size="25%" centerContent={Space.CenterType.HorizontalVertical} style={{ backgroundColor: "white" }}>
23+
<span style={{ color: "black" }}>Hello world</span>
24+
</Space.TopResizable>
25+
<Space.BottomResizable size="25%" centerContent={Space.CenterType.HorizontalVertical} style={{ backgroundColor: "white" }}>
26+
<span style={{ color: "black" }}>Hello world</span>
27+
</Space.BottomResizable>
28+
<Space.Fill centerContent={Space.CenterType.HorizontalVertical}>Hello world</Space.Fill>
1929
</Space.Fill>
2030
</Space.ViewPort>
2131
</>

rollup.config.js

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const targets = [
2626
plugins: [
2727
postcss({
2828
extract: false,
29+
minimize: true,
2930
}),
3031
...commonPlugins,
3132
babel({ babelHelpers: "runtime", exclude: "node_modules/**" }),
@@ -40,6 +41,7 @@ const targets = [
4041
plugins: [
4142
postcss({
4243
extract: false,
44+
minimize: true,
4345
}),
4446
...commonPlugins,
4547
],
@@ -52,6 +54,7 @@ const targets = [
5254
plugins: [
5355
postcss({
5456
extract: true,
57+
minimize: true,
5558
}),
5659
...commonPlugins,
5760
babel({ babelHelpers: "runtime", exclude: "node_modules/**" }),
@@ -66,6 +69,7 @@ const targets = [
6669
plugins: [
6770
postcss({
6871
extract: true,
72+
minimize: true,
6973
}),
7074
...commonPlugins,
7175
],

src/Globals.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module "*.css";

src/components/Positioned.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ interface IPositionedProps extends IReactSpaceCommonProps {
1919
export const Positioned: React.FC<IPositionedProps> = ({ left, top, right, bottom, width, height, resizable, ...props }) => {
2020
const resizeTypes = resizable || [];
2121

22-
console.log(resizeTypes);
23-
2422
return (
2523
<Space
2624
{...props}

src/components/SSR.tsx

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as React from "react";
2+
import { enabledSsrSupport } from "../core-react";
3+
import css from "../styles.css";
4+
5+
export const SSR: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
6+
enabledSsrSupport();
7+
return (
8+
<>
9+
<style dangerouslySetInnerHTML={{ __html: css }} />
10+
{children}
11+
</>
12+
);
13+
};

src/components/Space.tsx

+45-13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import { CenterType, ResizeHandlePlacement, AnchorType, Type } from "../core-types";
2-
import { useSpace, ParentContext, LayerContext, DOMRectContext, IReactSpaceInnerProps, useEffectOnce } from "../core-react";
2+
import {
3+
useSpace,
4+
ParentContext,
5+
LayerContext,
6+
DOMRectContext,
7+
IReactSpaceInnerProps,
8+
useEffectOnce,
9+
SSR_SUPPORT_ENABLED,
10+
useUniqueId,
11+
} from "../core-react";
312
import * as React from "react";
413
import { Centered } from "./Centered";
514
import { CenteredVertically } from "./CenteredVertically";
6-
import { shortuuid } from "../core-utils";
15+
import { isServer, updateStyleDefinition } from "../core-utils";
716

817
function applyCentering(children: React.ReactNode, centerType: CenterType | undefined) {
918
switch (centerType) {
@@ -22,8 +31,14 @@ export class Space extends React.Component<IReactSpaceInnerProps> {
2231
}
2332

2433
const SpaceInner: React.FC<IReactSpaceInnerProps & { wrapperInstance: Space }> = (props) => {
25-
if (!props.id && !props.wrapperInstance["_react_spaces_uniqueid"]) {
26-
props.wrapperInstance["_react_spaces_uniqueid"] = `s${shortuuid()}`;
34+
let idToUse = props.id ?? props.wrapperInstance["_react_spaces_uniqueid"];
35+
const [initialRender, setInitialRender] = React.useState(SSR_SUPPORT_ENABLED ? true : false);
36+
37+
const uniqueId = useUniqueId();
38+
39+
if (!idToUse) {
40+
props.wrapperInstance["_react_spaces_uniqueid"] = uniqueId;
41+
idToUse = props.wrapperInstance["_react_spaces_uniqueid"];
2742
}
2843

2944
const {
@@ -56,11 +71,25 @@ const SpaceInner: React.FC<IReactSpaceInnerProps & { wrapperInstance: Space }> =
5671

5772
const { space, domRect, elementRef, resizeHandles } = useSpace({
5873
...props,
59-
...{ id: props.id || props.wrapperInstance["_react_spaces_uniqueid"] },
74+
...{ id: idToUse },
6075
});
6176

77+
if (SSR_SUPPORT_ENABLED && !isServer()) {
78+
const preRenderedStyle = document.getElementById(`style_${idToUse}_ssr`);
79+
if (preRenderedStyle) {
80+
space.ssrStyle = preRenderedStyle.innerHTML;
81+
}
82+
updateStyleDefinition(space);
83+
}
84+
6285
useEffectOnce(() => {
6386
space.element = elementRef.current!;
87+
88+
if (SSR_SUPPORT_ENABLED) {
89+
if (initialRender) {
90+
setInitialRender(false);
91+
}
92+
}
6493
});
6594

6695
const userClasses = className ? className.split(" ").map((c) => c.trim()) : [];
@@ -89,19 +118,22 @@ const SpaceInner: React.FC<IReactSpaceInnerProps & { wrapperInstance: Space }> =
89118

90119
const centeredContent = applyCentering(children, props.centerContent);
91120

121+
const outerProps = {
122+
...{
123+
id: space.id,
124+
ref: elementRef,
125+
className: outerClasses.join(" "),
126+
},
127+
...events,
128+
} as any;
129+
92130
return (
93131
<>
94132
{resizeHandles.mouseHandles.map((handleProps) => handleRender?.(handleProps) || <div {...handleProps} />)}
133+
{SSR_SUPPORT_ENABLED && space.ssrStyle && initialRender && <style id={`style_${space.id}_ssr`}>{space.ssrStyle}</style>}
95134
{React.createElement(
96135
props.as || "div",
97-
{
98-
...{
99-
id: space.id,
100-
ref: elementRef,
101-
className: outerClasses.join(" "),
102-
},
103-
...events,
104-
},
136+
outerProps,
105137
<div className={innerClasses.join(" ")} style={innerStyle}>
106138
<ParentContext.Provider value={space.id}>
107139
<LayerContext.Provider value={undefined}>

src/components/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export * from "./Layer";
1010
export * from "./Positioned";
1111
export * from "./SpaceInfo";
1212
export * from "./ViewPort";
13+
export * from "./SSR";
1314
export * from "./Options";

src/core-react.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,28 @@ export function useForceUpdate() {
117117
}, []);
118118
}
119119

120+
export function useUniqueId() {
121+
if (SSR_SUPPORT_ENABLED) {
122+
if (React.version.startsWith("18")) {
123+
return `s${React.useId().replace(/\:/g, "")}`;
124+
}
125+
126+
if ((React as any).unstable_useOpaqueIdentifier) {
127+
return `s${(React as any).unstable_useOpaqueIdentifier().replace(/\:/g, "")}`;
128+
}
129+
}
130+
131+
return `s${shortuuid()}`;
132+
}
133+
120134
export function useSpace(props: IReactSpaceInnerProps) {
121135
const store = currentStore;
122136
const update = useForceUpdate();
123137
const parent = React.useContext(ParentContext);
124138
const layer = React.useContext(LayerContext);
125139
const { debug } = React.useContext(OptionsContext);
126-
const [spaceId] = React.useState(props.id || `s${shortuuid()}`);
140+
const uniqueId = useUniqueId();
141+
const [spaceId] = React.useState(props.id || uniqueId);
127142
const elementRef = React.useRef<HTMLElement>();
128143
const resizeSensor = React.useRef<ResizeSensor>();
129144
const [domRect, setDomRect] = React.useState<DOMRect>();
@@ -275,3 +290,8 @@ export function useCurrentSpace() {
275290
forceUpdate: onForceUpdate,
276291
} as ISpaceContext;
277292
}
293+
294+
export let SSR_SUPPORT_ENABLED = false;
295+
export function enabledSsrSupport() {
296+
SSR_SUPPORT_ENABLED = true;
297+
}

src/core-types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ export interface ISpaceDefinition {
171171
canResizeBottomLeft: boolean;
172172
canResizeBottomRight: boolean;
173173
allowOverflow: boolean;
174+
ssrStyle: string;
174175
}
175176

176177
export interface ISpaceContext {
@@ -179,4 +180,5 @@ export interface ISpaceContext {
179180
startMouseDrag: (e: ResizeMouseEvent, onDragEnd?: OnDragEnd) => void;
180181
startTouchDrag: (e: ResizeTouchEvent, onDragEnd?: OnDragEnd) => void;
181182
forceUpdate: () => void;
183+
ssrStyle?: string;
182184
}

src/core-utils.ts

+21-13
Original file line numberDiff line numberDiff line change
@@ -395,21 +395,20 @@ export function styleDefinition(space: ISpaceDefinition) {
395395

396396
export function updateStyleDefinition(space: ISpaceDefinition) {
397397
const definition = styleDefinition(space);
398-
if (typeof document !== "undefined") {
399-
if (document) {
400-
const existing = document.getElementById(`style_${space.id}`);
401-
402-
if (existing) {
403-
if (existing.innerHTML !== definition) {
404-
existing.innerHTML = definition;
405-
}
406-
} else {
407-
const newStyle = document.createElement("style");
408-
newStyle.id = `style_${space.id}`;
409-
newStyle.innerHTML = definition;
410-
document.head.appendChild(newStyle);
398+
if (!isServer()) {
399+
const existing = document.getElementById(`style_${space.id}`);
400+
if (existing) {
401+
if (existing.innerHTML !== definition) {
402+
existing.innerHTML = definition;
411403
}
404+
} else {
405+
const newStyle = document.createElement("style");
406+
newStyle.id = `style_${space.id}`;
407+
newStyle.innerHTML = definition;
408+
document.head.appendChild(newStyle);
412409
}
410+
} else {
411+
space.ssrStyle = definition;
413412
}
414413
}
415414

@@ -419,3 +418,12 @@ export function removeStyleDefinition(space: ISpaceDefinition) {
419418
document.head.removeChild(existing);
420419
}
421420
}
421+
422+
export function isServer() {
423+
if (typeof document !== "undefined") {
424+
if (document) {
425+
return false;
426+
}
427+
}
428+
return true;
429+
}

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ export type {
2222
ResizeTouchEvent
2323
}
2424

25-
export { useCurrentSpace } from "./core-react";
25+
export { useCurrentSpace, enabledSsrSupport } from "./core-react";

0 commit comments

Comments
 (0)