Step towards full multiplayer compatibility
This commit is contained in:
parent
9895a286a3
commit
53a07b21b0
16 changed files with 207 additions and 104 deletions
|
@ -1,4 +1,4 @@
|
|||
import { EventBarModes, Target } from "../../interfaces/frontend"
|
||||
import { EventBarModes } from "../../interfaces/frontend"
|
||||
import Item from "./Item"
|
||||
import { GameSettings } from "@components/Lobby/SettingsFrame/Setting"
|
||||
import {
|
||||
|
@ -17,6 +17,7 @@ import {
|
|||
import { useDrawProps } from "@hooks/useDrawProps"
|
||||
import { useGameProps } from "@hooks/useGameProps"
|
||||
import { socket } from "@lib/socket"
|
||||
import { GamePropsSchema } from "@lib/zodSchemas"
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
|
@ -26,30 +27,38 @@ import {
|
|||
useState,
|
||||
} from "react"
|
||||
|
||||
function EventBar({
|
||||
props: { setMode, setTarget, clear },
|
||||
}: {
|
||||
props: {
|
||||
setMode: Dispatch<SetStateAction<number>>
|
||||
setTarget: Dispatch<SetStateAction<Target>>
|
||||
clear: () => void
|
||||
}
|
||||
}) {
|
||||
const [menu, setMenu] = useState<keyof EventBarModes>("main")
|
||||
const { shouldHide, color } = useDrawProps()
|
||||
const { payload, setSetting, full } = useGameProps()
|
||||
|
||||
const gameSetting = useCallback(
|
||||
(payload: GameSettings) => {
|
||||
export function setGameSetting(
|
||||
payload: GameSettings,
|
||||
setSetting: (settings: GameSettings) => string | null,
|
||||
full: (payload: GamePropsSchema) => void
|
||||
) {
|
||||
return () => {
|
||||
const hash = setSetting(payload)
|
||||
socket.emit("gameSetting", payload, (newHash) => {
|
||||
if (newHash === hash) return
|
||||
console.log("hash", hash, newHash)
|
||||
socket.emit("update", full)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function EventBar({
|
||||
props: { setMode, clear },
|
||||
}: {
|
||||
props: {
|
||||
setMode: Dispatch<SetStateAction<number>>
|
||||
clear: () => void
|
||||
}
|
||||
}) {
|
||||
const [menu, setMenu] = useState<keyof EventBarModes>("main")
|
||||
const { shouldHide, color } = useDrawProps()
|
||||
const { payload, setSetting, full, setTarget } = useGameProps()
|
||||
|
||||
const gameSetting = useCallback(
|
||||
(payload: GameSettings) => setGameSetting(payload, setSetting, full),
|
||||
[full, setSetting]
|
||||
)
|
||||
|
||||
const items = useMemo<EventBarModes>(
|
||||
() => ({
|
||||
main: [
|
||||
|
@ -168,33 +177,31 @@ function EventBar({
|
|||
icon: faGlasses,
|
||||
text: "Spectators",
|
||||
disabled: !payload?.game?.allowSpectators,
|
||||
callback: () => {
|
||||
gameSetting({ allowSpectators: !payload?.game?.allowSpectators })
|
||||
},
|
||||
callback: gameSetting({
|
||||
allowSpectators: !payload?.game?.allowSpectators,
|
||||
}),
|
||||
},
|
||||
{
|
||||
icon: faSparkles,
|
||||
text: "Specials",
|
||||
disabled: !payload?.game?.allowSpecials,
|
||||
callback: () => {
|
||||
gameSetting({ allowSpecials: !payload?.game?.allowSpecials })
|
||||
},
|
||||
callback: gameSetting({
|
||||
allowSpecials: !payload?.game?.allowSpecials,
|
||||
}),
|
||||
},
|
||||
{
|
||||
icon: faComments,
|
||||
text: "Chat",
|
||||
disabled: !payload?.game?.allowChat,
|
||||
callback: () => {
|
||||
gameSetting({ allowChat: !payload?.game?.allowChat })
|
||||
},
|
||||
callback: gameSetting({ allowChat: !payload?.game?.allowChat }),
|
||||
},
|
||||
{
|
||||
icon: faScribble,
|
||||
text: "Mark/Draw",
|
||||
disabled: !payload?.game?.allowMarkDraw,
|
||||
callback: () => {
|
||||
gameSetting({ allowMarkDraw: !payload?.game?.allowMarkDraw })
|
||||
},
|
||||
callback: gameSetting({
|
||||
allowMarkDraw: !payload?.game?.allowMarkDraw,
|
||||
}),
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Hit, MouseCursor, Target } from "../../interfaces/frontend"
|
||||
import { MouseCursor } from "../../interfaces/frontend"
|
||||
// import Bluetooth from "./Bluetooth"
|
||||
// import FogImages from "./FogImages"
|
||||
import Labeling from "./Labeling"
|
||||
|
@ -9,28 +9,29 @@ import HitElems from "@components/Gamefield/HitElems"
|
|||
import Targets from "@components/Gamefield/Targets"
|
||||
import { useDraw } from "@hooks/useDraw"
|
||||
import { useDrawProps } from "@hooks/useDrawProps"
|
||||
import { useGameProps } from "@hooks/useGameProps"
|
||||
import {
|
||||
hitReducer,
|
||||
initlialTarget,
|
||||
initlialTargetPreview,
|
||||
initlialMouseCursor,
|
||||
overlapsWithAnyBorder,
|
||||
isAlreadyHit,
|
||||
targetList,
|
||||
} from "@lib/utils/helpers"
|
||||
import { CSSProperties, useCallback } from "react"
|
||||
import { useEffect, useReducer, useState } from "react"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
export const count = 12
|
||||
|
||||
function Gamefield() {
|
||||
const [target, setTarget] = useState<Target>(initlialTarget)
|
||||
const [targetPreview, setTargetPreview] = useState<Target>(
|
||||
initlialTargetPreview
|
||||
)
|
||||
const [mouseCursor, setMouseCursor] =
|
||||
useState<MouseCursor>(initlialMouseCursor)
|
||||
const [hits, DispatchHits] = useReducer(hitReducer, [] as Hit[])
|
||||
const {
|
||||
hits,
|
||||
target,
|
||||
targetPreview,
|
||||
DispatchHits,
|
||||
setTarget,
|
||||
setTargetPreview,
|
||||
} = useGameProps()
|
||||
const [mode, setMode] = useState(0)
|
||||
|
||||
const settingTarget = useCallback(
|
||||
|
@ -54,7 +55,7 @@ function Gamefield() {
|
|||
} else if (!overlapsWithAnyBorder(targetPreview, mode))
|
||||
setTarget({ show: true, x, y })
|
||||
},
|
||||
[hits, mode, target, targetPreview]
|
||||
[DispatchHits, hits, mode, setTarget, target, targetPreview]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -68,7 +69,7 @@ function Gamefield() {
|
|||
show: !show || x !== position.x || y !== position.y,
|
||||
})
|
||||
}
|
||||
}, [mode, mouseCursor, target])
|
||||
}, [mode, mouseCursor, setTargetPreview, target])
|
||||
|
||||
const { canvasRef, onMouseDown, clear } = useDraw()
|
||||
const { enable, color, shouldHide } = useDrawProps()
|
||||
|
@ -113,7 +114,7 @@ function Gamefield() {
|
|||
height="648"
|
||||
/>
|
||||
</div>
|
||||
<EventBar props={{ setMode, setTarget, clear }} />
|
||||
<EventBar props={{ setMode, clear }} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
import { Ship } from "../../interfaces/frontend"
|
||||
import classNames from "classnames"
|
||||
import { CSSProperties } from "react"
|
||||
|
||||
function Ships() {
|
||||
let shipIndexes = [
|
||||
{ size: 2, index: null },
|
||||
{ size: 3, index: 1 },
|
||||
{ size: 3, index: 2 },
|
||||
{ size: 3, index: 3 },
|
||||
{ size: 4, index: 1 },
|
||||
{ size: 4, index: 2 },
|
||||
const shipIndexes: Ship[] = [
|
||||
{ size: 2, variant: 1, x: 3, y: 3 },
|
||||
{ size: 3, variant: 1, x: 4, y: 3 },
|
||||
{ size: 3, variant: 2, x: 5, y: 3 },
|
||||
{ size: 3, variant: 3, x: 6, y: 3 },
|
||||
{ size: 4, variant: 1, x: 7, y: 3 },
|
||||
{ size: 4, variant: 2, x: 8, y: 3 },
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
{shipIndexes.map(({ size, index }, i) => {
|
||||
const filename = `/assets/ship_blue_${size}x${
|
||||
index ? "_" + index : ""
|
||||
}.gif`
|
||||
{shipIndexes.map(({ size, variant, x, y }, i) => {
|
||||
const filename = `ship_blue_${size}x_${variant}.gif`
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={classNames("ship", "s" + size)}
|
||||
style={{ "--x": i + 3 } as CSSProperties}
|
||||
style={{ "--x": x, "--y": y } as CSSProperties}
|
||||
>
|
||||
<img src={filename} alt={filename} />
|
||||
<img src={"/assets/" + filename} alt={filename} />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { setGameSetting } from "@components/Gamefield/EventBar"
|
||||
import {
|
||||
faToggleLargeOff,
|
||||
faToggleLargeOn,
|
||||
|
@ -17,12 +18,12 @@ export type GameSettings = { [key in GameSettingKeys]?: boolean }
|
|||
|
||||
function Setting({
|
||||
children,
|
||||
props: { prop, gameSetting },
|
||||
prop,
|
||||
}: {
|
||||
children: ReactNode
|
||||
props: { prop: GameSettingKeys; gameSetting: (payload: GameSettings) => void }
|
||||
prop: GameSettingKeys
|
||||
}) {
|
||||
const { payload } = useGameProps()
|
||||
const { payload, setSetting, full } = useGameProps()
|
||||
const state = useMemo(() => payload?.game?.[prop], [payload?.game, prop])
|
||||
|
||||
return (
|
||||
|
@ -46,12 +47,15 @@ function Setting({
|
|||
checked={state}
|
||||
type="checkbox"
|
||||
id={prop}
|
||||
onChange={() => {
|
||||
const payload = {
|
||||
onChange={() =>
|
||||
setGameSetting(
|
||||
{
|
||||
[prop]: !state,
|
||||
},
|
||||
setSetting,
|
||||
full
|
||||
)
|
||||
}
|
||||
gameSetting(payload)
|
||||
}}
|
||||
hidden={true}
|
||||
/>
|
||||
</label>
|
||||
|
|
|
@ -44,15 +44,14 @@ function Settings({ closeSettings }: { closeSettings: () => void }) {
|
|||
<div className="flex items-center justify-end">
|
||||
<button
|
||||
className="right-12 top-8 h-14 w-14"
|
||||
onClick={() => {
|
||||
const payload = {
|
||||
onClick={() =>
|
||||
gameSetting({
|
||||
allowSpectators: true,
|
||||
allowSpecials: true,
|
||||
allowChat: true,
|
||||
allowMarkDraw: true,
|
||||
})
|
||||
}
|
||||
gameSetting(payload)
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
className="h-full w-full text-gray-800 drop-shadow-md"
|
||||
|
@ -62,18 +61,10 @@ function Settings({ closeSettings }: { closeSettings: () => void }) {
|
|||
</button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-8">
|
||||
<Setting props={{ gameSetting, prop: "allowSpectators" }}>
|
||||
Erlaube Zuschauer
|
||||
</Setting>
|
||||
<Setting props={{ gameSetting, prop: "allowSpecials" }}>
|
||||
Erlaube spezial Items
|
||||
</Setting>
|
||||
<Setting props={{ gameSetting, prop: "allowChat" }}>
|
||||
Erlaube den Chat
|
||||
</Setting>
|
||||
<Setting props={{ gameSetting, prop: "allowMarkDraw" }}>
|
||||
Erlaube zeichen/makieren
|
||||
</Setting>
|
||||
<Setting prop="allowSpectators">Erlaube Zuschauer</Setting>
|
||||
<Setting prop="allowSpecials">Erlaube spezial Items</Setting>
|
||||
<Setting prop="allowChat">Erlaube den Chat</Setting>
|
||||
<Setting prop="allowMarkDraw">Erlaube zeichen/makieren</Setting>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Draw, Point } from "../interfaces/frontend"
|
||||
import { Draw, DrawLineProps, Point } from "../interfaces/frontend"
|
||||
import { useDrawProps } from "./useDrawProps"
|
||||
import { socket } from "@lib/socket"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
||||
function onDraw({ prevPoint, currentPoint, ctx, color }: Draw) {
|
||||
function drawLine({ prevPoint, currentPoint, ctx, color }: Draw) {
|
||||
const { x: currX, y: currY } = currentPoint
|
||||
const lineColor = color
|
||||
const lineWidth = 5
|
||||
|
@ -52,7 +53,7 @@ export const useDraw = () => {
|
|||
const ctx = canvasRef.current?.getContext("2d")
|
||||
if (!ctx || !currentPoint) return
|
||||
|
||||
onDraw({ ctx, currentPoint, prevPoint: prevPoint.current, color })
|
||||
drawLine({ ctx, currentPoint, prevPoint: prevPoint.current, color })
|
||||
prevPoint.current = currentPoint
|
||||
}
|
||||
|
||||
|
@ -80,5 +81,39 @@ export const useDraw = () => {
|
|||
}
|
||||
}, [color, mouseDown])
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current
|
||||
if (!canvas) return
|
||||
|
||||
const ctx = canvas.getContext("2d")
|
||||
if (!ctx) return
|
||||
|
||||
socket.on("playerEvent", (event) => {
|
||||
if (!canvasRef.current?.toDataURL() || event.type !== "connect") return
|
||||
console.log("sending canvas state")
|
||||
socket.emit("canvas-state", canvasRef.current.toDataURL())
|
||||
})
|
||||
|
||||
socket.on("canvas-state-from-server", (state: string, index) => {
|
||||
console.log("I received the state")
|
||||
const img = new Image()
|
||||
img.src = state
|
||||
img.onload = () => {
|
||||
ctx?.drawImage(img, 0, 0)
|
||||
}
|
||||
})
|
||||
|
||||
socket.on("draw-line", ({ prevPoint, currentPoint, color }, index) => {
|
||||
if (!ctx) return console.log("no ctx here")
|
||||
drawLine({ prevPoint, currentPoint, ctx, color })
|
||||
})
|
||||
|
||||
socket.on("clear", clear)
|
||||
|
||||
return () => {
|
||||
socket.removeAllListeners()
|
||||
}
|
||||
})
|
||||
|
||||
return { canvasRef, onMouseDown, clear }
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { Hit, HitDispatch, Target } from "../interfaces/frontend"
|
||||
import { GameSettings } from "@components/Lobby/SettingsFrame/Setting"
|
||||
import { getPayloadwithChecksum } from "@lib/getPayloadwithChecksum"
|
||||
import { socket } from "@lib/socket"
|
||||
import { initlialTarget, initlialTargetPreview } from "@lib/utils/helpers"
|
||||
import {
|
||||
GamePropsSchema,
|
||||
optionalGamePropsSchema,
|
||||
PlayerSchema,
|
||||
} from "@lib/zodSchemas"
|
||||
import { produce } from "immer"
|
||||
import { SetStateAction } from "react"
|
||||
import { toast } from "react-toastify"
|
||||
import { create } from "zustand"
|
||||
import { devtools } from "zustand/middleware"
|
||||
|
@ -16,9 +19,15 @@ const initialState: optionalGamePropsSchema & {
|
|||
isReady: boolean
|
||||
isConnected: boolean
|
||||
}[]
|
||||
hits: Hit[]
|
||||
target: Target
|
||||
targetPreview: Target
|
||||
} = {
|
||||
payload: null,
|
||||
hash: null,
|
||||
hits: [],
|
||||
target: initlialTarget,
|
||||
targetPreview: initlialTargetPreview,
|
||||
userStates: Array.from(Array(2), () => ({
|
||||
isReady: false,
|
||||
isConnected: false,
|
||||
|
@ -28,8 +37,11 @@ const initialState: optionalGamePropsSchema & {
|
|||
export type State = typeof initialState
|
||||
|
||||
export type Action = {
|
||||
setSetting: (settings: GameSettings) => string | null
|
||||
DispatchHits: (action: HitDispatch) => void
|
||||
setTarget: (target: SetStateAction<Target>) => void
|
||||
setTargetPreview: (targetPreview: SetStateAction<Target>) => void
|
||||
setPlayer: (payload: { users: PlayerSchema[] }) => string | null
|
||||
setSetting: (settings: GameSettings) => string | null
|
||||
full: (newProps: GamePropsSchema) => void
|
||||
leave: (cb: () => void) => void
|
||||
setIsReady: (payload: { i: number; isReady: boolean }) => void
|
||||
|
@ -41,6 +53,34 @@ export const useGameProps = create<State & Action>()(
|
|||
devtools(
|
||||
(set) => ({
|
||||
...initialState,
|
||||
DispatchHits: (action) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
switch (action.type) {
|
||||
case "fireMissile":
|
||||
case "htorpedo":
|
||||
case "vtorpedo": {
|
||||
state.hits.push(...action.payload)
|
||||
}
|
||||
}
|
||||
})
|
||||
),
|
||||
setTarget: (target) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (typeof target === "function")
|
||||
state.target = target(state.target)
|
||||
else state.target = target
|
||||
})
|
||||
),
|
||||
setTargetPreview: (targetPreview) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (typeof targetPreview === "function")
|
||||
state.targetPreview = targetPreview(state.target)
|
||||
else state.targetPreview = targetPreview
|
||||
})
|
||||
),
|
||||
setPlayer: (payload) => {
|
||||
let hash: string | null = null
|
||||
set(
|
||||
|
|
|
@ -17,7 +17,6 @@ function useSocket() {
|
|||
full,
|
||||
setIsReady,
|
||||
setIsConnected,
|
||||
hash: stateHash,
|
||||
} = useGameProps()
|
||||
const { data: session } = useSession()
|
||||
const router = useRouter()
|
||||
|
@ -39,7 +38,6 @@ function useSocket() {
|
|||
|
||||
useEffect(() => {
|
||||
if (!session?.user.id) return
|
||||
socket.connect()
|
||||
|
||||
socket.on("connect", () => {
|
||||
console.log("connected")
|
||||
|
@ -115,9 +113,7 @@ function useSocket() {
|
|||
})
|
||||
})
|
||||
|
||||
socket.on("isReady", (payload) => {
|
||||
setIsReady(payload)
|
||||
})
|
||||
socket.on("isReady", setIsReady)
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
console.log("disconnect")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { DrawLineProps } from "./frontend"
|
||||
import { GameSettings } from "@components/Lobby/SettingsFrame/Setting"
|
||||
import { GamePropsSchema, PlayerSchema } from "@lib/zodSchemas"
|
||||
import type { Server as HTTPServer } from "http"
|
||||
|
@ -43,6 +44,10 @@ export interface ServerToClientEvents {
|
|||
) => void
|
||||
isReady: (payload: { i: number; isReady: boolean }) => void
|
||||
isConnected: (payload: { i: number; isConnected: boolean }) => void
|
||||
"get-canvas-state": () => void
|
||||
"canvas-state-from-server": (state: string, userIndex: number) => void
|
||||
"draw-line": (props: DrawLineProps, userIndex: number) => void
|
||||
clear: () => void
|
||||
}
|
||||
|
||||
export interface ClientToServerEvents {
|
||||
|
@ -53,6 +58,9 @@ export interface ClientToServerEvents {
|
|||
join: (withAck: (ack: boolean) => void) => void
|
||||
gameSetting: (payload: GameSettings, callback: (hash: string) => void) => void
|
||||
leave: (withAck: (ack: boolean) => void) => void
|
||||
"canvas-state": (state: string) => void
|
||||
"draw-line": (props: DrawLineProps) => void
|
||||
clear: () => void
|
||||
}
|
||||
|
||||
interface InterServerEvents {
|
||||
|
|
|
@ -64,9 +64,17 @@ export interface Point {
|
|||
y: number
|
||||
}
|
||||
|
||||
export interface Draw {
|
||||
ctx: CanvasRenderingContext2D
|
||||
export interface DrawLineProps {
|
||||
currentPoint: Point
|
||||
prevPoint: Point | null
|
||||
color: string
|
||||
}
|
||||
|
||||
export interface Draw extends DrawLineProps {
|
||||
ctx: CanvasRenderingContext2D
|
||||
}
|
||||
|
||||
export interface Ship extends Position {
|
||||
size: number
|
||||
variant: number
|
||||
}
|
||||
|
|
|
@ -3,5 +3,4 @@ import { io } from "socket.io-client"
|
|||
|
||||
export const socket: cSocket = io({
|
||||
path: "/api/ws",
|
||||
autoConnect: false,
|
||||
})
|
||||
|
|
|
@ -26,19 +26,6 @@ export function cornerCN(count: number, x: number, y: number) {
|
|||
export function fieldIndex(count: number, x: number, y: number) {
|
||||
return y * (count + 2) + x
|
||||
}
|
||||
export function hitReducer(formObject: Hit[], action: HitDispatch) {
|
||||
switch (action.type) {
|
||||
case "fireMissile":
|
||||
case "htorpedo":
|
||||
case "vtorpedo": {
|
||||
const result = [...formObject, ...action.payload]
|
||||
return result
|
||||
}
|
||||
|
||||
default:
|
||||
return formObject
|
||||
}
|
||||
}
|
||||
|
||||
const modes: Mode[] = [
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@ import {
|
|||
NextApiResponseWithSocket,
|
||||
sServer,
|
||||
} from "../../interfaces/NextApiSocket"
|
||||
import { DrawLineProps } from "../../interfaces/frontend"
|
||||
import {
|
||||
composeBody,
|
||||
gameSelects,
|
||||
|
@ -183,6 +184,33 @@ const SocketHandler = async (
|
|||
.emit("isConnected", { i: socket.data.index, isConnected: true })
|
||||
})
|
||||
|
||||
socket.on("canvas-state", (state) => {
|
||||
if (!socket.data.gameId || !socket.data.index) return
|
||||
console.log("received canvas state")
|
||||
socket
|
||||
.to(socket.data.gameId)
|
||||
.emit("canvas-state-from-server", state, socket.data.index)
|
||||
})
|
||||
|
||||
socket.on(
|
||||
"draw-line",
|
||||
({ prevPoint, currentPoint, color }: DrawLineProps) => {
|
||||
if (!socket.data.gameId || !socket.data.index) return
|
||||
socket
|
||||
.to(socket.data.gameId)
|
||||
.emit(
|
||||
"draw-line",
|
||||
{ prevPoint, currentPoint, color },
|
||||
socket.data.index
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
socket.on("clear", () => {
|
||||
if (!socket.data.gameId) return
|
||||
socket.to(socket.data.gameId).emit("clear")
|
||||
})
|
||||
|
||||
socket.on("disconnecting", async () => {
|
||||
logging(
|
||||
"Disconnecting: " + JSON.stringify(Array.from(socket.rooms)),
|
||||
|
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
@ -120,15 +120,15 @@ body {
|
|||
}
|
||||
|
||||
&.s2 {
|
||||
grid-column: 3 / 5;
|
||||
grid-column: var(--y) / 5;
|
||||
}
|
||||
|
||||
&.s3 {
|
||||
grid-column: 3 / 6;
|
||||
grid-column: var(--y) / 6;
|
||||
}
|
||||
|
||||
&.s4 {
|
||||
grid-column: 3 / 7;
|
||||
grid-column: var(--y) / 7;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue