UNPKG

@tldraw/editor

Version:

tldraw infinite canvas SDK (editor).

189 lines (188 loc) • 5.65 kB
import { atom } from "@tldraw/state"; import { getDefaultTranslationLocale } from "@tldraw/tlschema"; import { getFromLocalStorage, setInLocalStorage, structuredClone, uniqueId } from "@tldraw/utils"; import { T } from "@tldraw/validate"; const USER_DATA_KEY = "TLDRAW_USER_DATA_v3"; const userTypeValidator = T.object({ id: T.string, name: T.string.nullable().optional(), color: T.string.nullable().optional(), // N.B. These are duplicated in TLdrawAppUser. locale: T.string.nullable().optional(), animationSpeed: T.number.nullable().optional(), areKeyboardShortcutsEnabled: T.boolean.nullable().optional(), edgeScrollSpeed: T.number.nullable().optional(), colorScheme: T.literalEnum("light", "dark", "system").optional(), isSnapMode: T.boolean.nullable().optional(), isWrapMode: T.boolean.nullable().optional(), isDynamicSizeMode: T.boolean.nullable().optional(), isPasteAtCursorMode: T.boolean.nullable().optional() }); const Versions = { AddAnimationSpeed: 1, AddIsSnapMode: 2, MakeFieldsNullable: 3, AddEdgeScrollSpeed: 4, AddExcalidrawSelectMode: 5, AddDynamicSizeMode: 6, AllowSystemColorScheme: 7, AddPasteAtCursor: 8, AddKeyboardShortcuts: 9 }; const CURRENT_VERSION = Math.max(...Object.values(Versions)); function migrateSnapshot(data) { if (data.version < Versions.AddAnimationSpeed) { data.user.animationSpeed = 1; } if (data.version < Versions.AddIsSnapMode) { data.user.isSnapMode = false; } if (data.version < Versions.MakeFieldsNullable) { } if (data.version < Versions.AddEdgeScrollSpeed) { data.user.edgeScrollSpeed = 1; } if (data.version < Versions.AddExcalidrawSelectMode) { data.user.isWrapMode = false; } if (data.version < Versions.AllowSystemColorScheme) { if (data.user.isDarkMode === true) { data.user.colorScheme = "dark"; } else if (data.user.isDarkMode === false) { data.user.colorScheme = "light"; } delete data.user.isDarkMode; } if (data.version < Versions.AddDynamicSizeMode) { data.user.isDynamicSizeMode = false; } if (data.version < Versions.AddPasteAtCursor) { data.user.isPasteAtCursorMode = false; } if (data.version < Versions.AddKeyboardShortcuts) { data.user.areKeyboardShortcutsEnabled = true; } data.version = CURRENT_VERSION; } const USER_COLORS = [ "#FF802B", "#EC5E41", "#F2555A", "#F04F88", "#E34BA9", "#BD54C6", "#9D5BD2", "#7B66DC", "#02B1CC", "#11B3A3", "#39B178", "#55B467" ]; function getRandomColor() { return USER_COLORS[Math.floor(Math.random() * USER_COLORS.length)]; } function userPrefersReducedMotion() { if (typeof window !== "undefined" && "matchMedia" in window) { return window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches ?? false; } return false; } const defaultUserPreferences = Object.freeze({ name: "", locale: getDefaultTranslationLocale(), color: getRandomColor(), // N.B. These are duplicated in TLdrawAppUser. edgeScrollSpeed: 1, animationSpeed: userPrefersReducedMotion() ? 0 : 1, areKeyboardShortcutsEnabled: true, isSnapMode: false, isWrapMode: false, isDynamicSizeMode: false, isPasteAtCursorMode: false, colorScheme: "light" }); function getFreshUserPreferences() { return { id: uniqueId(), color: getRandomColor() }; } function migrateUserPreferences(userData) { if (userData === null || typeof userData !== "object") { return getFreshUserPreferences(); } if (!("version" in userData) || !("user" in userData) || typeof userData.version !== "number") { return getFreshUserPreferences(); } const snapshot = structuredClone(userData); migrateSnapshot(snapshot); try { return userTypeValidator.validate(snapshot.user); } catch { return getFreshUserPreferences(); } } function loadUserPreferences() { const userData = JSON.parse(getFromLocalStorage(USER_DATA_KEY) || "null") ?? null; return migrateUserPreferences(userData); } const globalUserPreferences = atom("globalUserData", null); function storeUserPreferences() { setInLocalStorage( USER_DATA_KEY, JSON.stringify({ version: CURRENT_VERSION, user: globalUserPreferences.get() }) ); } function setUserPreferences(user) { userTypeValidator.validate(user); globalUserPreferences.set(user); storeUserPreferences(); broadcastUserPreferencesChange(); } const isTest = typeof process !== "undefined" && process.env.NODE_ENV === "test"; const channel = typeof BroadcastChannel !== "undefined" && !isTest ? new BroadcastChannel("tldraw-user-sync") : null; channel?.addEventListener("message", (e) => { const data = e.data; if (data?.type === broadcastEventKey && data?.origin !== getBroadcastOrigin()) { globalUserPreferences.set(migrateUserPreferences(data.data)); } }); let _broadcastOrigin = null; function getBroadcastOrigin() { if (_broadcastOrigin === null) { _broadcastOrigin = uniqueId(); } return _broadcastOrigin; } const broadcastEventKey = "tldraw-user-preferences-change"; function broadcastUserPreferencesChange() { channel?.postMessage({ type: broadcastEventKey, origin: getBroadcastOrigin(), data: { user: getUserPreferences(), version: CURRENT_VERSION } }); } function getUserPreferences() { let prefs = globalUserPreferences.get(); if (!prefs) { prefs = loadUserPreferences(); setUserPreferences(prefs); } return prefs; } export { USER_COLORS, defaultUserPreferences, getFreshUserPreferences, getUserPreferences, setUserPreferences, userPrefersReducedMotion, userTypeValidator }; //# sourceMappingURL=TLUserPreferences.mjs.map